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_subordinate #(int CosimToRenodeIndex = 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.peripherals[CosimToRenodeIndex].reset_assert_request) begin
26    bus.rvalid = 0;
27    bus.bvalid = 0;
28    bus.areset_n = 0;
29    // The reset takes 2 cycles to prevent a race condition without usage of a non-blocking assigment.
30    repeat (2) @(posedge clk);
31    runtime.peripherals[CosimToRenodeIndex].reset_assert_respond();
32  end
33
34  always @(runtime.peripherals[CosimToRenodeIndex].reset_deassert_request) begin
35    bus.areset_n = 1;
36    // There is one more wait for the clock edges to be sure that all modules aren't in a reset state.
37    repeat (2) @(posedge clk);
38    runtime.peripherals[CosimToRenodeIndex].reset_deassert_respond();
39  end
40
41  always @(clk) read_transaction();
42  always @(clk) write_transaction();
43
44  task static read_transaction();
45    transaction_id_t transaction_id;
46    address_t address;
47    renode_pkg::data_t data;
48    bit is_error;
49    burst_size_t burst_size;
50    burst_length_t burst_length;
51    burst_type_e burst_type;
52    address_t address_last;
53    renode_pkg::valid_bits_e valid_bits;
54    address_t transfer_bytes;
55
56    get_read_address(transaction_id, address, burst_size, burst_length, burst_type);
57    valid_bits = bus.burst_size_to_valid_bits(burst_size);
58    transfer_bytes = 2**burst_size;
59    address_last = address + transfer_bytes * burst_length;
60    for (; address <= address_last; address += transfer_bytes) begin
61      if(!is_access_valid(address, valid_bits, burst_type)) begin
62          set_read_response(transaction_id, 0, SlaveError, address == address_last);
63      end
64      else begin
65          // The conection.read call may cause simulation time to move forward
66          runtime.peripherals[CosimToRenodeIndex].read(renode_pkg::address_t'(address), valid_bits, data, is_error);
67          if (is_error) begin
68            runtime.connection.log(LogWarning, $sformatf("Unable to read data from Renode at address 'h%h, responding with 0.", address));
69            data = 0;
70          end
71          data = data & valid_bits;
72          set_read_response(transaction_id, data_t'(data) << ((address % transfer_bytes) * 8), is_error ? SlaveError : Okay, address == address_last);
73      end
74    end
75  endtask
76
77  task static write_transaction();
78    transaction_id_t transaction_id;
79    address_t address;
80    data_t data;
81    bit is_error;
82    bit last_transfer;
83    burst_size_t burst_size;
84    burst_length_t burst_length;
85    burst_type_e burst_type;
86    address_t address_last;
87    renode_pkg::valid_bits_e valid_bits;
88    address_t transfer_bytes;
89
90    get_write_address(transaction_id, address, burst_size, burst_length, burst_type);
91    valid_bits = bus.burst_size_to_valid_bits(burst_size);
92    if(!is_access_valid(address, valid_bits, burst_type)) begin
93      do @(posedge clk); while (!bus.wvalid);
94      bus.wready <= 1;
95      @(posedge clk);
96      bus.wready <= 0;
97      set_write_response(transaction_id, SlaveError);
98    end
99    else begin
100      transfer_bytes = 2**burst_size;
101      address_last = address + transfer_bytes * burst_length;
102
103
104      for (; address <= address_last; address += transfer_bytes) begin
105        do @(posedge clk); while (!bus.wvalid);
106        bus.wready <= 1;
107        data = bus.wdata >> ((address % transfer_bytes) * 8);
108
109        @(posedge clk);
110        bus.wready <= 0;
111
112        if (bus.wlast != (address == address_last)) runtime.connection.log(LogWarning, "Unexpected state of the wlast signal.");
113        // The conection.write call may cause simulation time to move forward
114        runtime.peripherals[CosimToRenodeIndex].write(renode_pkg::address_t'(address), valid_bits, renode_pkg::data_t'(data) & valid_bits, is_error);
115        if (is_error) runtime.connection.log(LogWarning, $sformatf("Unable to write data to Renode at address 'h%h", address));
116      end
117
118      set_write_response(transaction_id, Okay);
119    end
120  endtask
121
122  function static is_access_valid(address_t address, renode_pkg::valid_bits_e valid_bits, burst_type_e burst_type);
123    if(!renode_pkg::is_access_aligned(renode_pkg::address_t'(address), valid_bits)) begin
124      runtime.connection.log(LogWarning, $sformatf("Unaligned access to 0x%08X unsupported by AXI Subordinate. This will result in bus error response.", address));
125      return 0;
126    end
127    if(burst_type != Incrementing) begin
128      runtime.connection.fatal_error($sformatf("Unsupported burst type 'b%b", burst_type));
129      return 0;
130    end
131    return 1;
132  endfunction
133
134  task static get_read_address(output transaction_id_t transaction_id, output address_t address,
135                               output burst_size_t burst_size, output burst_length_t burst_length, output burst_type_e burst_type);
136    @(posedge clk);
137    bus.arready <= 1;
138
139    do @(posedge clk); while (!bus.arvalid);
140    transaction_id = bus.arid;
141    address = bus.araddr;
142    burst_size = bus.arsize;
143    burst_length = bus.arlen;
144    burst_type = burst_type_e'(bus.arburst);
145    bus.arready <= 0;
146  endtask
147
148  task static get_write_address(output transaction_id_t transaction_id, output address_t address,
149                                output burst_size_t burst_size, output burst_length_t burst_length, output burst_type_e burst_type);
150    @(posedge clk);
151    bus.awready <= 1;
152
153    do @(posedge clk); while (!bus.awvalid);
154    transaction_id = bus.awid;
155    address = bus.awaddr;
156    burst_size = bus.awsize;
157    burst_length = bus.awlen;
158    burst_type = burst_type_e'(bus.awburst);
159    bus.awready <= 0;
160  endtask
161
162  task static set_read_response(transaction_id_t transaction_id, data_t data, response_e response, bit last);
163        @(posedge clk);
164        bus.rid <= transaction_id;
165        bus.rdata <= data;
166        bus.rresp <= response;
167        bus.rlast <= last;
168        bus.rvalid <= 1;
169
170        // It's required to assert the valid and ready signals only for one clock cycle.
171        do @(posedge clk); while (!bus.rready);
172        bus.rlast  <= 0;
173        bus.rvalid <= 0;
174  endtask
175
176  task static set_write_response(transaction_id_t id, response_e response);
177    bus.bid   = id;
178    bus.bresp = response;
179
180    @(posedge clk);
181    bus.bvalid <= 1;
182
183    do @(posedge clk); while (!bus.bready);
184    bus.bvalid <= 0;
185  endtask
186endmodule
187
188