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_completer #(
13    parameter int unsigned OutputLatency = 0,
14    int CosimToRenodeIndex = 0
15) (
16    ref renode_runtime runtime,
17    renode_apb3_if bus
18);
19
20  typedef logic [bus.AddressWidth-1:0] address_t;
21  typedef logic [bus.DataWidth-1:0] data_t;
22
23  // Renaming the bus is a style preference
24  wire clk;
25  assign clk = bus.pclk;
26  logic rst_n;
27  assign bus.presetn = rst_n;
28
29  address_t paddr;
30  logic     pselx;
31  logic     penable;
32  logic     pwrite;
33  data_t    pwdata;
34  logic     pready;
35  data_t    prdata;
36  logic     pslverr;
37
38  assign paddr = bus.paddr;
39  assign pselx = bus.pselx;
40  assign penable = bus.penable;
41  assign pwrite = bus.pwrite;
42  assign pwdata = bus.pwdata;
43
44  assign bus.pready = pready;
45  assign bus.prdata = prdata;
46  assign bus.pslverr = pslverr;
47
48  // Connection initiated reset
49  always @(runtime.peripherals[CosimToRenodeIndex].reset_assert_request) begin
50    rst_n = 0;
51    // The reset takes 2 cycles to prevent a race condition without usage of a non-blocking assigment.
52    repeat (2) @(posedge clk);
53    runtime.peripherals[CosimToRenodeIndex].reset_assert_respond();
54  end
55
56  always @(runtime.peripherals[CosimToRenodeIndex].reset_deassert_request) begin
57    rst_n = 1;
58    // There is one more wait for the clock edges to be sure that all modules aren't in a reset state.
59    repeat (2) @(posedge clk);
60    runtime.peripherals[CosimToRenodeIndex].reset_deassert_respond();
61  end
62
63  renode_pkg::valid_bits_e valid_bits;
64  assign valid_bits = renode_pkg::valid_bits_e'((1 << bus.DataWidth) - 1);
65
66  //
67  // APB3 Completer
68  //
69
70  bit is_error;
71  renode_pkg::data_t prdata_int;
72
73  // Internal state
74  typedef enum {
75    STATE_IDLE,
76    STATE_ACCESS
77  } state_t;
78  state_t peripheral_state;
79  state_t peripheral_state_next;
80
81  // Next state logic
82  always_comb begin : proc_fsm_next_state
83    case (peripheral_state)
84      STATE_IDLE: begin
85        if (pselx && !penable) begin
86          peripheral_state_next = STATE_ACCESS;
87        end
88      end
89      STATE_ACCESS: begin
90        peripheral_state_next = STATE_IDLE;
91      end
92      default: begin
93        peripheral_state_next = STATE_IDLE;
94      end
95    endcase
96  end
97
98  // FSM logic
99  always_ff @(posedge clk or negedge rst_n) begin : proc_fsm
100    if (rst_n == 1'b0) begin
101      peripheral_state <= STATE_IDLE;
102    end else begin
103      peripheral_state <= peripheral_state_next;
104
105      // Write Enable logic: write data to memory in Renode
106      if (pselx && penable && pwrite) begin
107        // Workaround::Bug::Verilator::Task call inside of always block requires using fork...join
108        fork
109          begin
110            runtime.peripherals[CosimToRenodeIndex].write(renode_pkg::address_t'(paddr), valid_bits, renode_pkg::data_t'(pwdata),
111                             is_error);
112            if (is_error) begin
113              runtime.connection.log(LogWarning, "Renode connection write transfer was unable to complete");
114            end
115          end
116        join
117      end
118
119      // Read Enable logic: read data from memory in Renode
120      if (pselx && !penable && !pwrite) begin
121        // Workaround::Bug::Verilator::Task call inside of always block requires using fork...join
122        fork
123          begin
124            // The runtime.peripherals[CosimToRenodeIndex].read call may cause elapse of a simulation time.
125            runtime.peripherals[CosimToRenodeIndex].read(renode_pkg::address_t'(paddr), valid_bits, prdata_int, is_error);
126            if (is_error) begin
127              runtime.connection.log(LogWarning, "Renode connection read transfer was unable to complete");
128            end
129          end
130        join
131      end else begin
132        prdata_int <= '0;
133      end
134    end
135  end
136
137  //
138  // Generate artificial delay to the {PRDATA,PREADY} signals
139  // Useful to validate wait states, by default is turned off.
140  //
141  genvar i;
142  generate
143    if (OutputLatency == 0) begin : gen_latency_0
144      assign prdata = data_t'(prdata_int);
145      assign pready = (peripheral_state == STATE_ACCESS);
146    end else begin : gen_latency_gt_0
147      data_t prdata_reg[OutputLatency];
148      data_t pready_reg[OutputLatency];
149      for (i = 0; i < OutputLatency; i++) begin : gen_output_registers
150        if (i == 0) begin : gen_i_eq_0
151          always_ff @(posedge clk or negedge rst_n) begin : proc_first_reg
152            prdata_reg[i] <= prdata_int;
153            pready_reg[i] <= (peripheral_state == STATE_ACCESS);
154          end
155        end else begin: gen_i_neq_0
156          always_ff @(posedge clk or negedge rst_n) begin : proc_latency_ith
157            prdata_reg[i] <= prdata_reg[i-1];
158            pready_reg[i] <= pready_reg[i-1];
159          end
160        end
161      end
162      assign prdata = prdata_reg[OutputLatency-1];
163      assign pready = pready_reg[OutputLatency-1];
164    end
165  endgenerate
166
167endmodule
168
169