1 //
2 // Copyright (c) 2010-2025 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 #pragma once
8 
9 #include <memory>
10 
11 #include "tlm.h"
12 #include "tlm_utils/simple_initiator_socket.h"
13 #include "tlm_utils/simple_target_socket.h"
14 
15 struct CTCPClient;
16 struct renode_message;
17 
18 #ifndef RENODE_BUSWIDTH
19 #define RENODE_BUSWIDTH 32
20 #endif
21 
22 #define NUM_GPIO 64
23 #define NUM_DIRECT_CONNECTIONS 4
24 
25 // ================================================================================
26 // renode_bridge
27 //
28 //   SystemC module that serves as an interface with Renode.
29 // ================================================================================
30 
31 class renode_bridge : sc_core::sc_module {
32 public:
33   renode_bridge(sc_core::sc_module_name name, const char *address,
34                 const char *port);
35   ~renode_bridge();
36 
37   // Returns true if connection with Renode has been established, false otherwise.
is_initialized()38   bool is_initialized() { return fw_connection_initialized; }
39 
40 public:
41   using renode_bus_target_socket =
42       tlm::tlm_target_socket<RENODE_BUSWIDTH, tlm::tlm_base_protocol_types, 1,
43                              sc_core::SC_ZERO_OR_MORE_BOUND>;
44   using renode_bus_initiator_socket =
45       tlm::tlm_initiator_socket<RENODE_BUSWIDTH, tlm::tlm_base_protocol_types,
46                                 1, sc_core::SC_ZERO_OR_MORE_BOUND>;
47   using gpio_in_port = sc_core::sc_port<sc_core::sc_signal_in_if<bool>, 1,
48                                         sc_core::SC_ZERO_OR_MORE_BOUND>;
49   using gpio_out_port = sc_core::sc_port<sc_core::sc_signal_out_if<bool>, 1,
50                                          sc_core::SC_ZERO_OR_MORE_BOUND>;
51   using reset_port = sc_core::sc_port<sc_core::sc_signal_inout_if<bool>, 1,
52                                       sc_core::SC_ZERO_OR_MORE_BOUND>;
53 
54   // Socket forwarding memory transactions performed in Renode to SystemC.
55   renode_bus_initiator_socket initiator_socket;
56 
57   // Socket forwarding register transactions performed in Renode to SystemC.
58   renode_bus_initiator_socket register_initiator_socket;
59 
60   // Socket forwarding transactions performed in SystemC to Renode.
61   renode_bus_target_socket target_socket;
62 
63   // Direct connections allow for binding peripherals directly to each other in
64   // Renode (bypassing System Bus).
65   renode_bus_initiator_socket
66       direct_connection_initiators[NUM_DIRECT_CONNECTIONS];
67   renode_bus_target_socket direct_connection_targets[NUM_DIRECT_CONNECTIONS];
68 
69   // Input GPIO ports - signal changes are driven by SystemC and propagated to
70   // Renode.
71   gpio_in_port gpio_ports_in[NUM_GPIO];
72 
73   // Output GPIO ports - signal changes are driven by Renode and propagated to
74   // SystemC.
75   gpio_out_port gpio_ports_out[NUM_GPIO];
76 
77   // Reset signal.
78   // Raised when the peripheral is reset. Expected to be lowered by SystemC
79   // once the reset process is complete.
80   reset_port reset;
81 
82   // Informs Renode CPU that memory has been modified in the given range. This
83   // is necessary when using DMI (get_direct_mem_ptr) to modify memory
84   // containing CPU instructions.
85   void invalidate_translation_blocks(uint64_t start_address, uint64_t end_address);
86 
87 private:
88   struct initiator_bw_handler: tlm::tlm_bw_transport_if<> {
89     initiator_bw_handler() = default;
90     void initialize(renode_bridge *);
91 
92     virtual tlm::tlm_sync_enum nb_transport_bw(tlm::tlm_generic_payload &trans,
93                                                tlm::tlm_phase &phase,
94                                                sc_core::sc_time &t);
95     virtual void invalidate_direct_mem_ptr(sc_dt::uint64 start_range,
96                                            sc_dt::uint64 end_range);
97 
98   private:
99     renode_bridge *bridge;
100   };
101 
102   struct target_fw_handler: tlm::tlm_fw_transport_if<> {
103     target_fw_handler() = default;
104 
105     renode_bus_target_socket socket;
106 
107     void initialize(renode_bridge *, uint8_t connection_idx);
108 
109     virtual void invalidate_direct_mem_ptr(sc_dt::uint64 start_range,
110                                            sc_dt::uint64 end_range);
111     virtual tlm::tlm_sync_enum
112     nb_transport_bw(tlm::tlm_generic_payload &payload, tlm::tlm_phase &phase,
113                     sc_core::sc_time &delta);
114     virtual tlm::tlm_sync_enum nb_transport_fw(tlm::tlm_generic_payload &trans,
115                                                tlm::tlm_phase &phase,
116                                                sc_core::sc_time &t);
117     virtual void b_transport(tlm::tlm_generic_payload &trans,
118                              sc_core::sc_time &delay);
119     virtual bool get_direct_mem_ptr(tlm::tlm_generic_payload &trans,
120                                     tlm::tlm_dmi &dmi_data);
121     virtual unsigned int transport_dbg(tlm::tlm_generic_payload &trans);
122 
123   private:
124     renode_bridge *bridge;
125     uint8_t connection_idx;
126   };
127 
128   void forward_loop();
129   void handle_read(renode_bus_initiator_socket &socket, renode_message &message, uint8_t data[8]);
130   void handle_write(renode_bus_initiator_socket &socket, renode_message &message, uint8_t data[8]);
131   void on_port_gpio();
132 
133   void update_backward_gpio_state(uint64_t new_gpio_state);
134   void service_backward_request(tlm::tlm_generic_payload &payload,
135                                 uint8_t connection_idx,
136                                 sc_core::sc_time &delay);
137   bool service_backward_request_dmi(tlm::tlm_generic_payload &payload,
138                                     tlm::tlm_dmi &dmi_data);
139   int64_t get_systemc_time_us();
140 
141   // Connection from Renode -> SystemC.
142   std::unique_ptr<CTCPClient> forward_connection;
143 
144   // Connection from SystemC -> Renode
145   std::unique_ptr<CTCPClient> backward_connection;
146 
147   // Construction/destruction of tlm_generic_payload is an expensive operation,
148   // so a single tlm_generic_payload object is reused, as recommended by OSCI
149   // TLM-2.0 Language Reference Manual. It also requires that the object is
150   // allocated on the heap.
151   std::unique_ptr<tlm::tlm_generic_payload> payload;
152 
153   initiator_bw_handler bus_initiator_bw_handler;
154   initiator_bw_handler cpu_initiator_bw_handler;
155   target_fw_handler bus_target_fw_handler;
156   target_fw_handler cpu_target_fw_handler;
157 
158   initiator_bw_handler dc_initiators[NUM_DIRECT_CONNECTIONS];
159   target_fw_handler dc_targets[NUM_DIRECT_CONNECTIONS];
160 
161   bool fw_connection_initialized;
162 };
163 
164 // ================================================================================
165