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_apb3_requester #(int RenodeToCosimIndex = 0) (
13    ref renode_runtime runtime,
14    renode_apb3_if bus
15);
16  typedef logic [bus.AddressWidth-1:0] address_t;
17  typedef logic [bus.DataWidth-1:0] data_t;
18
19  // Renaming the bus is a style preference
20  wire clk;
21  assign clk = bus.pclk;
22
23  logic rst_n;
24  assign bus.presetn = rst_n;
25
26  address_t paddr;
27  logic     pselx;
28  logic     penable;
29  logic     pwrite;
30  data_t    pwdata;
31  logic     pready;
32  data_t    prdata;
33  logic     pslverr;
34
35  assign bus.paddr = paddr;
36  assign bus.pselx = pselx;
37  assign bus.penable = penable;
38  assign bus.pwrite = pwrite;
39  assign bus.pwdata = pwdata;
40
41  assign pready = bus.pready;
42  assign prdata = bus.prdata;
43  assign pslverr = bus.pslverr;
44
45  int unsigned b2b_counter;
46  address_t write_address;
47  address_t read_address;
48  data_t write_data;
49
50  logic start_transaction;
51  logic write_mode;
52
53  // Only value of 1 is currently supported
54  localparam int unsigned Back2BackNum = 1;
55
56
57  always @(runtime.controllers[RenodeToCosimIndex].reset_assert_request) begin
58    write_address = '0;
59    read_address = '0;
60    write_data = '0;
61    start_transaction = '0;
62    write_mode = '0;
63    rst_n = 0;
64    // The reset takes 2 cycles to prevent a race condition without usage of a non-blocking assigment.
65    repeat (2) @(posedge clk);
66    runtime.controllers[RenodeToCosimIndex].reset_assert_respond();
67  end
68
69  always @(runtime.controllers[RenodeToCosimIndex].reset_deassert_request) begin
70    rst_n = 1;
71    // There is one more wait for the clock edges to be sure that all modules aren't in a reset state.
72    repeat (2) @(posedge clk);
73    runtime.controllers[RenodeToCosimIndex].reset_deassert_respond();
74  end
75
76  // Internal state
77  typedef enum {
78    S_IDLE,
79    S_SETUP,
80    S_ACCESS
81  } state_t;
82  state_t state = S_IDLE;
83
84  //
85  // Waveform generation
86  //
87
88  always @(runtime.controllers[RenodeToCosimIndex].read_transaction_request) begin
89    integer transaction_width;
90
91    if(!renode_pkg::is_access_aligned(runtime.controllers[RenodeToCosimIndex].read_transaction_address, runtime.controllers[RenodeToCosimIndex].read_transaction_data_bits)) begin
92        runtime.connection.log(LogWarning, "Unaligned access on APB bus results in unpredictable behavior");
93    end
94    transaction_width = renode_pkg::valid_bits_to_transaction_width(runtime.controllers[RenodeToCosimIndex].read_transaction_data_bits);
95    if (bus.DataWidth > transaction_width) begin
96      runtime.connection.log(LogWarning,
97          $sformatf("Bus bus.bus.DataWidth is (%d) > transaction width (%d), MSB will be truncated.", bus.DataWidth, transaction_width));
98    end else if (bus.DataWidth < transaction_width) begin
99      runtime.connection.log(LogWarning,
100          $sformatf("Bus bus.bus.DataWidth is (%d) < transaction width (%d), MSB will be zero-extended.", bus.DataWidth, transaction_width));
101    end
102
103    read_address = address_t'(runtime.controllers[RenodeToCosimIndex].read_transaction_address);
104    write_mode = 1'b0;
105    start_transaction = 1'b1;
106    @(posedge clk) start_transaction <= 1'b0;
107  end
108
109  always @(runtime.controllers[RenodeToCosimIndex].write_transaction_request) begin
110    integer transaction_width;
111
112    if(!renode_pkg::is_access_aligned(runtime.controllers[RenodeToCosimIndex].write_transaction_address, runtime.controllers[RenodeToCosimIndex].write_transaction_data_bits)) begin
113        runtime.connection.log(LogWarning, "Unaligned access on APB bus results in unpredictable behavior");
114    end
115    transaction_width = renode_pkg::valid_bits_to_transaction_width(runtime.controllers[RenodeToCosimIndex].write_transaction_data_bits);
116    if (bus.DataWidth > transaction_width) begin
117      runtime.connection.log(LogWarning,
118          $sformatf("Bus bus.bus.DataWidth is (%d) > transaction width (%d), MSB will be truncated.", bus.DataWidth, transaction_width));
119    end else if (bus.DataWidth < transaction_width) begin
120      runtime.connection.log(LogWarning,
121          $sformatf("Bus bus.bus.DataWidth is (%d) < transaction width (%d), MSB will be zero-extended.", bus.DataWidth, transaction_width));
122    end
123
124    write_address = address_t'(runtime.controllers[RenodeToCosimIndex].write_transaction_address);
125    write_data = data_t'(runtime.controllers[RenodeToCosimIndex].write_transaction_data);
126    write_mode = 1'b1;
127    start_transaction = 1'b1;
128    @(posedge clk) start_transaction <= 1'b0;
129  end
130
131  state_t next_state;
132  always_comb begin : proc_next_state
133    case (state)
134      S_IDLE: begin
135        if (start_transaction) begin
136          next_state = S_SETUP;
137        end else begin
138          next_state = S_IDLE;
139        end
140      end
141      S_SETUP: begin
142        next_state = S_ACCESS;
143      end
144      S_ACCESS: begin
145        if (pready) begin
146          if (b2b_counter == 0) begin
147            next_state = S_IDLE;
148          end else begin
149            next_state = S_SETUP;
150          end
151        end else begin
152          next_state = S_ACCESS;
153        end
154      end
155      default: begin
156        next_state = S_IDLE;
157      end
158    endcase
159  end
160
161  always_ff @(posedge clk or negedge rst_n) begin
162    if (rst_n == '0) begin
163      state <= S_IDLE;
164    end else begin
165      state <= next_state;
166
167      case (state)
168        S_IDLE: begin
169          b2b_counter <= Back2BackNum;
170        end
171        S_SETUP: begin
172          b2b_counter <= b2b_counter - 1;
173        end
174        S_ACCESS: begin
175          if (pready) begin
176            if (write_mode) begin
177              runtime.controllers[RenodeToCosimIndex].write_respond(1'b0);  // Notify Renode that write is done
178            end else begin
179              runtime.controllers[RenodeToCosimIndex].read_respond(renode_pkg::data_t'(prdata), 1'b0);
180            end
181          end
182        end
183        default: begin
184          b2b_counter <= Back2BackNum;
185        end
186      endcase
187    end
188  end
189
190  always_comb begin : proc_fsm_outputs
191    case (state)
192      S_IDLE: begin
193        paddr   = '0;
194        pselx   = '0;
195        penable = '0;
196        pwrite  = '0;
197        pwdata  = '0;
198      end
199      S_SETUP: begin
200        paddr   = write_mode ? write_address : read_address;
201        pselx   = 1'b1;
202        penable = 1'b0;
203        pwrite  = write_mode;
204        pwdata  = write_mode ? write_data : '0;
205      end
206      S_ACCESS: begin
207        paddr   = write_mode ? write_address : read_address;
208        pselx   = 1'b1;
209        penable = 1'b1;
210        pwrite  = write_mode;
211        pwdata  = write_mode ? write_data : '0;
212      end
213      default: begin
214        paddr   = '0;
215        pselx   = '0;
216        penable = '0;
217        pwrite  = '0;
218        pwdata  = '0;
219      end
220    endcase
221  end
222endmodule
223
224