| // |
| // 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_completer #( |
| parameter int unsigned OutputLatency = 0, |
| int CosimToRenodeIndex = 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 paddr = bus.paddr; |
| assign pselx = bus.pselx; |
| assign penable = bus.penable; |
| assign pwrite = bus.pwrite; |
| assign pwdata = bus.pwdata; |
| |
| assign bus.pready = pready; |
| assign bus.prdata = prdata; |
| assign bus.pslverr = pslverr; |
| |
| // Connection initiated reset |
| always @(runtime.peripherals[CosimToRenodeIndex].reset_assert_request) begin |
| 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.peripherals[CosimToRenodeIndex].reset_assert_respond(); |
| end |
| |
| always @(runtime.peripherals[CosimToRenodeIndex].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.peripherals[CosimToRenodeIndex].reset_deassert_respond(); |
| end |
| |
| renode_pkg::valid_bits_e valid_bits; |
| assign valid_bits = renode_pkg::valid_bits_e'((1 << bus.DataWidth) - 1); |
| |
| // |
| // APB3 Completer |
| // |
| |
| bit is_error; |
| renode_pkg::data_t prdata_int; |
| |
| // Internal state |
| typedef enum { |
| STATE_IDLE, |
| STATE_ACCESS |
| } state_t; |
| state_t peripheral_state; |
| state_t peripheral_state_next; |
| |
| // Next state logic |
| always_comb begin : proc_fsm_next_state |
| case (peripheral_state) |
| STATE_IDLE: begin |
| if (pselx && !penable) begin |
| peripheral_state_next = STATE_ACCESS; |
| end |
| end |
| STATE_ACCESS: begin |
| peripheral_state_next = STATE_IDLE; |
| end |
| default: begin |
| peripheral_state_next = STATE_IDLE; |
| end |
| endcase |
| end |
| |
| // FSM logic |
| always_ff @(posedge clk or negedge rst_n) begin : proc_fsm |
| if (rst_n == 1'b0) begin |
| peripheral_state <= STATE_IDLE; |
| end else begin |
| peripheral_state <= peripheral_state_next; |
| |
| // Write Enable logic: write data to memory in Renode |
| if (pselx && penable && pwrite) begin |
| // Workaround::Bug::Verilator::Task call inside of always block requires using fork...join |
| fork |
| begin |
| runtime.peripherals[CosimToRenodeIndex].write(renode_pkg::address_t'(paddr), valid_bits, renode_pkg::data_t'(pwdata), |
| is_error); |
| if (is_error) begin |
| runtime.connection.log(LogWarning, "Renode connection write transfer was unable to complete"); |
| end |
| end |
| join |
| end |
| |
| // Read Enable logic: read data from memory in Renode |
| if (pselx && !penable && !pwrite) begin |
| // Workaround::Bug::Verilator::Task call inside of always block requires using fork...join |
| fork |
| begin |
| // The runtime.peripherals[CosimToRenodeIndex].read call may cause elapse of a simulation time. |
| runtime.peripherals[CosimToRenodeIndex].read(renode_pkg::address_t'(paddr), valid_bits, prdata_int, is_error); |
| if (is_error) begin |
| runtime.connection.log(LogWarning, "Renode connection read transfer was unable to complete"); |
| end |
| end |
| join |
| end else begin |
| prdata_int <= '0; |
| end |
| end |
| end |
| |
| // |
| // Generate artificial delay to the {PRDATA,PREADY} signals |
| // Useful to validate wait states, by default is turned off. |
| // |
| genvar i; |
| generate |
| if (OutputLatency == 0) begin : gen_latency_0 |
| assign prdata = data_t'(prdata_int); |
| assign pready = (peripheral_state == STATE_ACCESS); |
| end else begin : gen_latency_gt_0 |
| data_t prdata_reg[OutputLatency]; |
| data_t pready_reg[OutputLatency]; |
| for (i = 0; i < OutputLatency; i++) begin : gen_output_registers |
| if (i == 0) begin : gen_i_eq_0 |
| always_ff @(posedge clk or negedge rst_n) begin : proc_first_reg |
| prdata_reg[i] <= prdata_int; |
| pready_reg[i] <= (peripheral_state == STATE_ACCESS); |
| end |
| end else begin: gen_i_neq_0 |
| always_ff @(posedge clk or negedge rst_n) begin : proc_latency_ith |
| prdata_reg[i] <= prdata_reg[i-1]; |
| pready_reg[i] <= pready_reg[i-1]; |
| end |
| end |
| end |
| assign prdata = prdata_reg[OutputLatency-1]; |
| assign pready = pready_reg[OutputLatency-1]; |
| end |
| endgenerate |
| |
| endmodule |
| |