1//
2// Copyright (c) 2010-2024 Antmicro
3//
4// This file is licensed under the MIT License.
5// Full license text is available in 'licenses/MIT.txt'.
6//
7
8`timescale 1ns / 1ps
9
10import renode_pkg::renode_runtime, renode_pkg::LogWarning;
11
12module renode_axi_manager #(int RenodeToCosimIndex = 0) (
13    ref renode_runtime runtime,
14    renode_axi_if bus
15);
16  import renode_axi_pkg::*;
17
18  typedef logic [bus.AddressWidth-1:0] address_t;
19  typedef logic [bus.DataWidth-1:0] data_t;
20  typedef logic [bus.StrobeWidth-1:0] strobe_t;
21  typedef logic [bus.TransactionIdWidth-1:0] transaction_id_t;
22
23  wire clk = bus.aclk;
24
25  always @(runtime.controllers[RenodeToCosimIndex].reset_assert_request) begin
26    bus.arvalid = 0;
27    bus.awvalid = 0;
28    bus.wvalid  = 0;
29    bus.areset_n = 0;
30    // The reset takes 2 cycles to prevent a race condition without usage of a non-blocking assigment.
31    repeat (2) @(posedge clk);
32    runtime.controllers[RenodeToCosimIndex].reset_assert_respond();
33  end
34
35  always @(runtime.controllers[RenodeToCosimIndex].reset_deassert_request) begin
36    bus.areset_n = 1;
37    // There is one more wait for the clock edges to be sure that all modules aren't in a reset state.
38    repeat (2) @(posedge clk);
39    runtime.controllers[RenodeToCosimIndex].reset_deassert_respond();
40  end
41
42  always @(runtime.controllers[RenodeToCosimIndex].read_transaction_request) read_transaction();
43  always @(runtime.controllers[RenodeToCosimIndex].write_transaction_request) write_transaction();
44
45  task static read_transaction();
46    bit is_error;
47    address_t address;
48    renode_pkg::valid_bits_e valid_bits;
49    burst_size_t burst_size;
50    data_t data;
51
52    address = address_t'(runtime.controllers[RenodeToCosimIndex].read_transaction_address);
53    valid_bits = runtime.controllers[RenodeToCosimIndex].read_transaction_data_bits;
54
55    if(!is_access_valid(address, valid_bits)) begin
56      runtime.controllers[RenodeToCosimIndex].read_respond(0, 1);
57    end else begin
58      burst_size = bus.valid_bits_to_burst_size(valid_bits);
59
60      read(0, address, burst_size, data, is_error);
61
62      data = data >> ((address % bus.StrobeWidth) * 8);
63      runtime.controllers[RenodeToCosimIndex].read_respond(renode_pkg::data_t'(data) & valid_bits, is_error);
64    end
65  endtask
66
67  task static write_transaction();
68    bit is_error;
69    address_t address;
70    renode_pkg::valid_bits_e valid_bits;
71    burst_size_t burst_size;
72    data_t data;
73    strobe_t strobe;
74
75    address = address_t'(runtime.controllers[RenodeToCosimIndex].write_transaction_address);
76    valid_bits = runtime.controllers[RenodeToCosimIndex].write_transaction_data_bits;
77
78    if(!is_access_valid(address, valid_bits)) begin
79      runtime.controllers[RenodeToCosimIndex].write_respond(1);
80    end else begin
81      burst_size = bus.valid_bits_to_burst_size(valid_bits);
82      data = data_t'(runtime.controllers[RenodeToCosimIndex].write_transaction_data & valid_bits);
83      strobe = bus.burst_size_to_strobe(burst_size) << (address % bus.StrobeWidth);
84      data = data << ((address % bus.StrobeWidth) * 8);
85
86      write(0, address, burst_size, strobe, data, is_error);
87
88      runtime.controllers[RenodeToCosimIndex].write_respond(is_error);
89    end
90  endtask
91
92  function static is_access_valid(address_t address, renode_pkg::valid_bits_e valid_bits);
93    if(!renode_pkg::is_access_aligned(renode_pkg::address_t'(address), valid_bits)) begin
94      runtime.connection.log(LogWarning, $sformatf("Unaligned access to 0x%08X is unsupported by AXI Manager. Operation will have no effect.", address));
95      return 0;
96    end
97    if(!bus.are_valid_bits_supported(valid_bits)) begin
98      runtime.connection.log(LogWarning, $sformatf("This instance of the AXI Manager doesn't support access using the 'b%b mask.", valid_bits));
99      return 0;
100    end
101    return 1;
102  endfunction
103
104  task static read(transaction_id_t id, address_t address, burst_size_t burst_size, output data_t data, output bit is_error);
105    transaction_id_t response_id;
106    response_e response;
107    fork
108      set_read_address(id, address, burst_size);
109      get_read_response(data, response_id, response);
110    join
111    is_error = check_response(id, response_id, response);
112  endtask
113
114  task static write(transaction_id_t id, address_t address, burst_size_t burst_size, strobe_t strobe, data_t data, output bit is_error);
115    transaction_id_t response_id;
116    response_e response;
117    fork
118      set_write_address(id, address, burst_size);
119      set_write_data(data, strobe);
120      get_write_response(response_id, response);
121    join
122    is_error = check_response(id, response_id, response);
123  endtask
124
125  task static set_read_address(transaction_id_t transaction_id, address_t address, burst_size_t burst_size);
126    bus.arid = transaction_id;
127    bus.araddr = address;
128    bus.arsize = burst_size;
129
130    // Configure transaction with only one burst.
131    bus.arlen = 0;
132    bus.arburst = 0;
133    bus.arlock = 0;
134    bus.arprot = 0;
135
136    @(posedge clk);
137    bus.arvalid <= 1;
138
139    do @(posedge clk); while (!bus.arready);
140    bus.arvalid <= 0;
141  endtask
142
143  task static get_read_response(output data_t data, output transaction_id_t transaction_id, output response_e response);
144    @(posedge clk);
145    bus.rready <= 1;
146
147    do @(posedge clk); while (!bus.rvalid);
148    data = bus.rdata;
149    transaction_id = bus.bid;
150    response = response_e'(bus.bresp);
151    bus.rready <= 0;
152  endtask
153
154  task static set_write_address(transaction_id_t id, address_t address, burst_size_t burst_size);
155    bus.awid = id;
156    bus.awaddr = address;
157    bus.awsize = burst_size;
158
159    // Configure transaction with only one burst.
160    bus.awlen = 0;
161    bus.awburst = 0;
162    bus.awlock = 0;
163    bus.awprot = 0;
164
165    @(posedge clk);
166    bus.awvalid <= 1;
167
168    do @(posedge clk); while (!bus.awready);
169    bus.awvalid <= 0;
170  endtask
171
172  task static set_write_data(data_t data, strobe_t strobe);
173    bus.wdata = data;
174    bus.wstrb = strobe;
175    bus.wlast = 1;
176
177    @(posedge clk);
178    bus.wvalid <= 1;
179
180    do @(posedge clk); while (!bus.wready);
181    bus.wvalid <= 0;
182  endtask
183
184  task static get_write_response(output transaction_id_t transaction_id, output response_e response);
185    @(posedge clk);
186    bus.bready <= 1;
187
188    do @(posedge clk); while (!bus.bvalid);
189    transaction_id = bus.bid;
190    response = response_e'(bus.bresp);
191    bus.bready <= 0;
192  endtask
193
194  function automatic bit check_response(transaction_id_t request_id, transaction_id_t response_id, response_t response);
195    response_e response_enum = response_e'(response);
196    if (response_id != request_id) begin
197      runtime.connection.log(LogWarning, $sformatf("Unexpected transaction id in the response ('h%h), expected 'h%h", response_id, request_id));
198      return 1;
199    end
200    if (response_enum != Okay && response_enum != ExclusiveAccessOkay) begin
201      runtime.connection.log(LogWarning, $sformatf("Response error 'h%h", response));
202      return 1;
203    end
204    return 0;
205  endfunction
206endmodule
207
208