1//
2// Copyright (c) 2023 Renesas Electronics Corporation
3// Copyright (c) 2010-2024 Antmicro
4//
5// This file is licensed under the MIT License.
6// Full license text is available in 'LICENSE'.
7//
8
9`timescale 1ns / 1ps
10
11import renode_pkg::renode_runtime, renode_pkg::LogWarning;
12
13module renode_ahb_subordinate #(int CosimToRenodeIndex = 0) (
14    ref renode_runtime runtime,
15    renode_ahb_if bus
16);
17  import renode_ahb_pkg::*;
18
19  typedef logic [bus.AddressWidth-1:0] address_t;
20  typedef logic [bus.DataWidth-1:0] data_t;
21  wire clk = bus.hclk;
22
23  always @(runtime.peripherals[CosimToRenodeIndex].reset_assert_request) begin
24    bus.hresetn = 0;
25    bus.hresp = Okay;
26    bus.hreadyout = 1;
27    repeat (2) @(posedge clk);
28    runtime.peripherals[CosimToRenodeIndex].reset_assert_respond();
29  end
30
31  always @(runtime.peripherals[CosimToRenodeIndex].reset_deassert_request) begin
32    bus.hresetn = 1;
33    repeat (2) @(posedge clk);
34    runtime.peripherals[CosimToRenodeIndex].reset_deassert_respond();
35  end
36
37  always @(posedge clk) transaction();
38
39  task static transaction();
40    renode_pkg::address_t address;
41    renode_pkg::valid_bits_e valid_bits;
42    renode_pkg::data_t data;
43    bit is_error;
44    bit is_invalid;
45    transfer_direction_e direction;
46
47    wait_for_transfer(address, valid_bits, direction, is_invalid);
48
49    // The runtime.peripherals[CosimToRenodeIndex].read call may consume an unknown number of clock cycles.
50    // To to make the logic simpler both read and write transactions contain at least one cycle with a deasserted ready.
51    // It also ensures that address and data phases don't overlap between transactions.
52    bus.hreadyout <= 0;
53    @(posedge clk);
54
55    if (!is_invalid) begin
56      if (direction == Read) begin
57        runtime.peripherals[CosimToRenodeIndex].read(address, valid_bits, data, is_error);
58        bus.hrdata = data_t'(data & valid_bits);
59        if (is_error) runtime.connection.log(LogWarning, $sformatf("Unable to read data from Renode at address 'h%h", address));
60      end else begin
61        runtime.peripherals[CosimToRenodeIndex].write(address, valid_bits, renode_pkg::data_t'(bus.hwdata) & valid_bits, is_error);
62        if (is_error) runtime.connection.log(LogWarning, $sformatf("Unable to write data to Renode at address 'h%h", address));
63      end
64    end
65
66    if (is_invalid || is_error) begin
67      bus.hresp = Error;
68      @(posedge clk); // An error response should last two cycles.
69      bus.hreadyout <= 1;
70    end
71    else begin
72      bus.hresp = Okay;
73      bus.hreadyout <= 1;
74    end
75  endtask
76
77
78  task static wait_for_transfer(output renode_pkg::address_t address, output renode_pkg::valid_bits_e valid_bits, output transfer_direction_e direction, output bit is_invalid);
79    is_invalid = 0;
80    bus.hreadyout <= 1;
81    while (!bus.hready || bus.htrans == Idle || bus.htrans == Busy) @(posedge clk);
82
83    address = bus.haddr;
84    valid_bits = bus.transfer_size_to_valid_bits(bus.hsize);
85    direction = transfer_direction_e'(bus.hwrite);
86    if (!bus.are_valid_bits_supported(valid_bits)) begin
87      runtime.connection.log(LogWarning, $sformatf("Unsupported transaction width of %d for AHB bus with width %d. No transaction will be performed.", renode_pkg::valid_bits_to_transaction_width(valid_bits), bus.DataWidth));
88      is_invalid = 1;
89    end
90  endtask
91endmodule
92