| // |
| // Copyright (c) 2010-2024 Antmicro |
| // |
| // This file is licensed under the MIT License. |
| // Full license text is available in 'licenses/MIT.txt'. |
| // |
| |
| `timescale 1ns / 1ps |
| |
| import renode_pkg::renode_runtime, renode_pkg::LogWarning; |
| |
| module renode_apb3_requester #(int RenodeToCosimIndex = 0) ( |
| ref renode_runtime runtime, |
| renode_apb3_if bus |
| ); |
| typedef logic [bus.AddressWidth-1:0] address_t; |
| typedef logic [bus.DataWidth-1:0] data_t; |
| |
| // Renaming the bus is a style preference |
| wire clk; |
| assign clk = bus.pclk; |
| |
| logic rst_n; |
| assign bus.presetn = rst_n; |
| |
| address_t paddr; |
| logic pselx; |
| logic penable; |
| logic pwrite; |
| data_t pwdata; |
| logic pready; |
| data_t prdata; |
| logic pslverr; |
| |
| assign bus.paddr = paddr; |
| assign bus.pselx = pselx; |
| assign bus.penable = penable; |
| assign bus.pwrite = pwrite; |
| assign bus.pwdata = pwdata; |
| |
| assign pready = bus.pready; |
| assign prdata = bus.prdata; |
| assign pslverr = bus.pslverr; |
| |
| int unsigned b2b_counter; |
| address_t write_address; |
| address_t read_address; |
| data_t write_data; |
| |
| logic start_transaction; |
| logic write_mode; |
| |
| // Only value of 1 is currently supported |
| localparam int unsigned Back2BackNum = 1; |
| |
| |
| always @(runtime.controllers[RenodeToCosimIndex].reset_assert_request) begin |
| write_address = '0; |
| read_address = '0; |
| write_data = '0; |
| start_transaction = '0; |
| write_mode = '0; |
| rst_n = 0; |
| // The reset takes 2 cycles to prevent a race condition without usage of a non-blocking assigment. |
| repeat (2) @(posedge clk); |
| runtime.controllers[RenodeToCosimIndex].reset_assert_respond(); |
| end |
| |
| always @(runtime.controllers[RenodeToCosimIndex].reset_deassert_request) begin |
| rst_n = 1; |
| // There is one more wait for the clock edges to be sure that all modules aren't in a reset state. |
| repeat (2) @(posedge clk); |
| runtime.controllers[RenodeToCosimIndex].reset_deassert_respond(); |
| end |
| |
| // Internal state |
| typedef enum { |
| S_IDLE, |
| S_SETUP, |
| S_ACCESS |
| } state_t; |
| state_t state = S_IDLE; |
| |
| // |
| // Waveform generation |
| // |
| |
| always @(runtime.controllers[RenodeToCosimIndex].read_transaction_request) begin |
| integer transaction_width; |
| |
| if(!renode_pkg::is_access_aligned(runtime.controllers[RenodeToCosimIndex].read_transaction_address, runtime.controllers[RenodeToCosimIndex].read_transaction_data_bits)) begin |
| runtime.connection.log(LogWarning, "Unaligned access on APB bus results in unpredictable behavior"); |
| end |
| transaction_width = renode_pkg::valid_bits_to_transaction_width(runtime.controllers[RenodeToCosimIndex].read_transaction_data_bits); |
| if (bus.DataWidth > transaction_width) begin |
| runtime.connection.log(LogWarning, |
| $sformatf("Bus bus.bus.DataWidth is (%d) > transaction width (%d), MSB will be truncated.", bus.DataWidth, transaction_width)); |
| end else if (bus.DataWidth < transaction_width) begin |
| runtime.connection.log(LogWarning, |
| $sformatf("Bus bus.bus.DataWidth is (%d) < transaction width (%d), MSB will be zero-extended.", bus.DataWidth, transaction_width)); |
| end |
| |
| read_address = address_t'(runtime.controllers[RenodeToCosimIndex].read_transaction_address); |
| write_mode = 1'b0; |
| start_transaction = 1'b1; |
| @(posedge clk) start_transaction <= 1'b0; |
| end |
| |
| always @(runtime.controllers[RenodeToCosimIndex].write_transaction_request) begin |
| integer transaction_width; |
| |
| if(!renode_pkg::is_access_aligned(runtime.controllers[RenodeToCosimIndex].write_transaction_address, runtime.controllers[RenodeToCosimIndex].write_transaction_data_bits)) begin |
| runtime.connection.log(LogWarning, "Unaligned access on APB bus results in unpredictable behavior"); |
| end |
| transaction_width = renode_pkg::valid_bits_to_transaction_width(runtime.controllers[RenodeToCosimIndex].write_transaction_data_bits); |
| if (bus.DataWidth > transaction_width) begin |
| runtime.connection.log(LogWarning, |
| $sformatf("Bus bus.bus.DataWidth is (%d) > transaction width (%d), MSB will be truncated.", bus.DataWidth, transaction_width)); |
| end else if (bus.DataWidth < transaction_width) begin |
| runtime.connection.log(LogWarning, |
| $sformatf("Bus bus.bus.DataWidth is (%d) < transaction width (%d), MSB will be zero-extended.", bus.DataWidth, transaction_width)); |
| end |
| |
| write_address = address_t'(runtime.controllers[RenodeToCosimIndex].write_transaction_address); |
| write_data = data_t'(runtime.controllers[RenodeToCosimIndex].write_transaction_data); |
| write_mode = 1'b1; |
| start_transaction = 1'b1; |
| @(posedge clk) start_transaction <= 1'b0; |
| end |
| |
| state_t next_state; |
| always_comb begin : proc_next_state |
| case (state) |
| S_IDLE: begin |
| if (start_transaction) begin |
| next_state = S_SETUP; |
| end else begin |
| next_state = S_IDLE; |
| end |
| end |
| S_SETUP: begin |
| next_state = S_ACCESS; |
| end |
| S_ACCESS: begin |
| if (pready) begin |
| if (b2b_counter == 0) begin |
| next_state = S_IDLE; |
| end else begin |
| next_state = S_SETUP; |
| end |
| end else begin |
| next_state = S_ACCESS; |
| end |
| end |
| default: begin |
| next_state = S_IDLE; |
| end |
| endcase |
| end |
| |
| always_ff @(posedge clk or negedge rst_n) begin |
| if (rst_n == '0) begin |
| state <= S_IDLE; |
| end else begin |
| state <= next_state; |
| |
| case (state) |
| S_IDLE: begin |
| b2b_counter <= Back2BackNum; |
| end |
| S_SETUP: begin |
| b2b_counter <= b2b_counter - 1; |
| end |
| S_ACCESS: begin |
| if (pready) begin |
| if (write_mode) begin |
| runtime.controllers[RenodeToCosimIndex].write_respond(1'b0); // Notify Renode that write is done |
| end else begin |
| runtime.controllers[RenodeToCosimIndex].read_respond(renode_pkg::data_t'(prdata), 1'b0); |
| end |
| end |
| end |
| default: begin |
| b2b_counter <= Back2BackNum; |
| end |
| endcase |
| end |
| end |
| |
| always_comb begin : proc_fsm_outputs |
| case (state) |
| S_IDLE: begin |
| paddr = '0; |
| pselx = '0; |
| penable = '0; |
| pwrite = '0; |
| pwdata = '0; |
| end |
| S_SETUP: begin |
| paddr = write_mode ? write_address : read_address; |
| pselx = 1'b1; |
| penable = 1'b0; |
| pwrite = write_mode; |
| pwdata = write_mode ? write_data : '0; |
| end |
| S_ACCESS: begin |
| paddr = write_mode ? write_address : read_address; |
| pselx = 1'b1; |
| penable = 1'b1; |
| pwrite = write_mode; |
| pwdata = write_mode ? write_data : '0; |
| end |
| default: begin |
| paddr = '0; |
| pselx = '0; |
| penable = '0; |
| pwrite = '0; |
| pwdata = '0; |
| end |
| endcase |
| end |
| endmodule |
| |