scc 2025.09
SystemC components library
target.h
1/*******************************************************************************
2 * Copyright 2021 MINRES Technologies GmbH
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *******************************************************************************/
16
17#pragma once
18
19#include <obi/obi_tlm.h>
20#include <scc/mt19937_rng.h>
21#include <scc/peq.h>
22#include <scc/report.h>
23#include <scc/signal_opt_ports.h>
24#include <systemc>
25#include <tlm/scc/initiator_mixin.h>
26#include <tlm/scc/scv/tlm_rec_initiator_socket.h>
27#include <tlm/scc/tlm_gp_shared.h>
28#include <tlm/scc/tlm_id.h>
29#include <tlm/scc/tlm_mm.h>
30#include <tlm>
31
32#include <memory>
33#include <queue>
34#include <tuple>
35#include <unordered_map>
36
37namespace obi {
38
39namespace pin {
40template <unsigned int DATA_WIDTH = 32, unsigned int ADDR_WIDTH = 32, unsigned int ID_WIDTH = 0, unsigned int USER_WIDTH = 0>
41class target : public sc_core::sc_module {
42public:
43 using payload_type = tlm::tlm_base_protocol_types::tlm_payload_type;
44 using phase_type = tlm::tlm_base_protocol_types::tlm_phase_type;
45
46 SC_HAS_PROCESS(target);
47
48 target(sc_core::sc_module_name nm);
49
51 // Global signals
52 sc_core::sc_in<bool> clk_i{"clk_i"};
53 sc_core::sc_in<bool> resetn_i{"resetn_i"}; // active low reset
54 // A Channel signals
55 sc_core::sc_in<bool> req_i{"req_i"};
56 sc_core::sc_out<bool> gnt_o{"gnt_o"};
57 sc_core::sc_in<sc_dt::sc_uint<ADDR_WIDTH>> addr_i{"addr_i"};
58 sc_core::sc_in<bool> we_i{"we_i"};
59 sc_core::sc_in<sc_dt::sc_uint<DATA_WIDTH / 8>> be_i{"be_i"};
60 sc_core::sc_in<sc_dt::sc_uint<DATA_WIDTH>> wdata_i{"wdata_i"};
64 // R Channel signals
65 sc_core::sc_out<bool> rvalid_o{"rvalid_o"};
66 sc_core::sc_in<bool> rready_i{"rready_i"};
67 sc_core::sc_out<sc_dt::sc_uint<DATA_WIDTH>> rdata_o{"rdata_o"};
68 sc_core::sc_out<bool> err_o{"err_o"};
71
72 tlm::tlm_sync_enum nb_transport_bw(payload_type& trans, phase_type& phase, sc_core::sc_time& t);
73
74 cci::cci_param<sc_core::sc_time> sample_delay{"sample_delay", 0_ns};
75 cci::cci_param<int> req2gnt_delay{"req2gnt_delay", 0};
76 cci::cci_param<int> addr2data_delay{"addr2data_delay", 0};
77
78private:
79 void clk_cb();
80 void achannel_req_t();
81 void rchannel_rsp_t();
82
83 void clk_delay() {
84 if(sc_core::sc_delta_count_at_current_time() < 5) {
85 clk_self.notify(sc_core::SC_ZERO_TIME);
86 next_trigger(clk_self);
87 } else
88 clk_delayed.notify(sc_core::SC_ZERO_TIME /*clk_if ? clk_if->period() - 1_ps : 1_ps*/);
89 }
90 sc_core::sc_event clk_delayed, clk_self;
92 std::deque<std::tuple<tlm::scc::tlm_gp_shared_ptr, unsigned>> rchannel_pending_rsp;
94 struct tx_state {
95 bool addrPhaseFinished;
96 tlm::tlm_phase last_phase;
97 tlm::scc::tlm_gp_shared_ptr pending_tx;
98 };
99 std::unordered_map<payload_type*, tx_state> states;
100};
101
103// Class definition
105template <unsigned int DATA_WIDTH, unsigned int ADDR_WIDTH, unsigned int ID_WIDTH, unsigned int USER_WIDTH>
107: sc_module(nm) {
108 isckt.register_nb_transport_bw([this](payload_type& trans, phase_type& phase, sc_core::sc_time& t) -> tlm::tlm_sync_enum {
109 return nb_transport_bw(trans, phase, t);
110 });
111 SC_METHOD(clk_cb)
112 sensitive << clk_i.pos() << resetn_i.neg();
113 SC_METHOD(clk_delay);
114 sensitive << clk_i.pos();
115 SC_THREAD(achannel_req_t)
116 SC_THREAD(rchannel_rsp_t);
117}
118
119template <unsigned int DATA_WIDTH, unsigned int ADDR_WIDTH, unsigned int ID_WIDTH, unsigned int USER_WIDTH>
120inline void target<DATA_WIDTH, ADDR_WIDTH, ID_WIDTH, USER_WIDTH>::target::clk_cb() {
121 sc_core::sc_time delay = sc_core::SC_ZERO_TIME;
122 if(clk_i.event()) {
123 if(rchannel_pending_rsp.size()) {
124 auto& head = rchannel_pending_rsp.front();
125 if(std::get<1>(head) == 0) {
126 rchannel_rsp.notify(std::get<0>(head));
127 rchannel_pending_rsp.pop_front();
128 }
129 for(auto& e : rchannel_pending_rsp) {
130 if(std::get<1>(e))
131 --std::get<1>(e);
132 }
133 }
134 }
135}
136
137template <unsigned int DATA_WIDTH, unsigned int ADDR_WIDTH, unsigned int ID_WIDTH, unsigned int USER_WIDTH>
138inline tlm::tlm_sync_enum
139target<DATA_WIDTH, ADDR_WIDTH, ID_WIDTH, USER_WIDTH>::target::nb_transport_bw(payload_type& trans, phase_type& phase, sc_core::sc_time& t) {
140 auto id = obi::get_obi_id(trans);
141 auto* ext = trans.get_extension<obi::obi_extension>();
142 sc_assert(ext && "obi_extension missing");
143 switch(phase) {
144 case tlm::END_REQ: {
145 auto it = states.find(&trans);
146 sc_assert(it != states.end());
147 it->second.last_phase = tlm::END_REQ;
148 achannel_rsp.notify(&trans, t);
149 return tlm::TLM_ACCEPTED;
150 }
151 case tlm::BEGIN_RESP: {
152 auto it = states.find(&trans);
153 sc_assert(it != states.end());
154 auto& state = it->second;
155 if(state.pending_tx) {
156 unsigned resp_delay = addr2data_delay < 0 ? scc::MT19937::uniform(0, -addr2data_delay) : addr2data_delay;
157 if(resp_delay) {
158 rchannel_pending_rsp.push_back({state.pending_tx, resp_delay - 1});
159 } else
160 rchannel_pending_rsp.push_back({state.pending_tx, 0});
161 }
162 state.last_phase = tlm::BEGIN_RESP;
163 return tlm::TLM_ACCEPTED;
164 }
165 default:
166 SCCWARN(SCMOD) << phase << " is unsupported phase transaction combination";
167 return tlm::TLM_ACCEPTED;
168 }
169}
170
171template <unsigned int DATA_WIDTH, unsigned int ADDR_WIDTH, unsigned int ID_WIDTH, unsigned int USER_WIDTH>
172inline void target<DATA_WIDTH, ADDR_WIDTH, ID_WIDTH, USER_WIDTH>::achannel_req_t() {
173 wait(SC_ZERO_TIME);
174 wait(clk_i.posedge_event());
175 while(true) {
176 while(resetn_i.read() == false)
177 wait(clk_i.posedge_event());
178 while(resetn_i.read() == true) {
179 gnt_o.write(req2gnt_delay == 0);
180 do {
181 wait(this->req_i.posedge_event() | clk_delayed);
182 } while(this->req_i.read() == false);
183 auto data_len = DATA_WIDTH / 8;
184 tlm::scc::tlm_gp_shared_ptr gp = tlm::scc::tlm_mm<>::get().allocate<obi::obi_extension>(data_len);
185 gp->set_streaming_width(data_len);
186 gp->set_address(addr_i.read());
187 gp->set_command(we_i.read() ? tlm::TLM_WRITE_COMMAND : tlm::TLM_READ_COMMAND);
188 auto be = static_cast<unsigned>(be_i.read());
189 auto cnt = util::bit_count(be);
190 gp->set_streaming_width(cnt);
191 gp->set_data_length(cnt);
192 if(we_i.read()) {
193 auto bus_data = wdata_i.read();
194 auto tx_byte_idx = 0;
195 for(auto i = 0U, be_idx = 0U; i < DATA_WIDTH; i += 8, ++be_idx) {
196 if(be & (1U << be_idx)) {
197 *(gp->get_data_ptr() + tx_byte_idx) = bus_data.range(i + 7, i).to_uint();
198 tx_byte_idx++;
199 }
200 }
201 SCCTRACE(SCMOD) << "Got write request to address 0x" << std::hex << gp->get_address();
202 } else
203 SCCTRACE(SCMOD) << "Got read request to address 0x" << std::hex << gp->get_address();
204 auto ext = gp->get_extension<obi::obi_extension>();
205 if(ID_WIDTH && aid_i.get_interface())
206 ext->set_id(aid_i->read());
207 if(USER_WIDTH) {
208 if(auser_i.get_interface())
209 ext->set_auser(auser_i->read());
210 if(wuser_i.get_interface() && we_i.read())
211 ext->set_duser(wuser_i->read());
212 }
213 auto& state = states[gp.get()];
214 phase_type phase = tlm::BEGIN_REQ;
215 auto delay = sc_core::SC_ZERO_TIME;
216 auto ret = isckt->nb_transport_fw(*gp, phase, delay);
217 auto startResp = false;
218 state.last_phase = phase;
219 if(ret == tlm::TLM_ACCEPTED) {
220 SCCTRACE(SCMOD) << "waiting for TLM reponse";
221 auto resp = achannel_rsp.get();
222 sc_assert(resp.get() == gp.get());
223 }
224 unsigned gnt_delay = req2gnt_delay < 0 ? scc::MT19937::uniform(0, -req2gnt_delay) : req2gnt_delay;
225 for(unsigned i = 0U; i < gnt_delay; ++i)
226 wait(clk_i.posedge_event());
227 gnt_o.write(true);
228 wait(clk_i.posedge_event());
229 state.addrPhaseFinished = true;
230 if(state.last_phase == tlm::BEGIN_RESP) {
231 unsigned resp_delay = addr2data_delay < 0 ? scc::MT19937::uniform(0, -addr2data_delay) : addr2data_delay;
232 if(resp_delay) {
233 rchannel_pending_rsp.push_back({gp, resp_delay - 1});
234 } else
235 rchannel_rsp.notify(gp);
236 } else {
237 state.pending_tx = gp;
238 }
239 }
240 }
241}
242
243template <unsigned int DATA_WIDTH, unsigned int ADDR_WIDTH, unsigned int ID_WIDTH, unsigned int USER_WIDTH>
244inline void target<DATA_WIDTH, ADDR_WIDTH, ID_WIDTH, USER_WIDTH>::rchannel_rsp_t() {
245 rdata_o.write(0);
246 err_o.write(false);
247 if(ID_WIDTH && r_id_o.get_interface())
248 r_id_o->write(0);
249 if(USER_WIDTH && ruser_o.get_interface())
250 ruser_o->write(0);
251 while(true) {
252 rvalid_o.write(false);
253 tlm::scc::tlm_gp_shared_ptr tx = rchannel_rsp.get();
254 if(tx->get_command() == tlm::TLM_READ_COMMAND) {
255 auto offset = tx->get_address() % (DATA_WIDTH / 8);
256 auto rx_data{sc_dt::sc_uint<DATA_WIDTH>(0)};
257 for(unsigned i = 0U, j = offset * 8; i < tx->get_data_length(); ++i, j += 8) {
258 rx_data.range(j + 7, j) = *(tx->get_data_ptr() + i);
259 }
260 rdata_o.write(rx_data);
261 SCCTRACE(SCMOD) << "responding to read request to address 0x" << std::hex << tx->get_address();
262 } else
263 SCCTRACE(SCMOD) << "responding to write request to address 0x" << std::hex << tx->get_address();
264 err_o.write(tx->get_response_status() != tlm::TLM_OK_RESPONSE);
265 if(ID_WIDTH || USER_WIDTH) {
266 auto ext = tx->get_extension<obi::obi_extension>();
267 if(ID_WIDTH && r_id_o.get_interface())
268 r_id_o->write(ext->get_id());
269 if(USER_WIDTH && ruser_o.get_interface())
270 ruser_o->write(ext->get_duser());
271 }
272 rvalid_o.write(true);
273 wait(sample_delay); // let rready settle
274 while(!rready_i.read())
275 wait(rready_i.value_changed_event());
276 phase_type phase = tlm::END_RESP;
277 auto delay = sc_core::SC_ZERO_TIME;
278 auto ret = isckt->nb_transport_fw(*tx, phase, delay);
279 auto it = states.find(tx.get());
280 sc_assert(it != states.end());
281 states.erase(it);
282 wait(clk_i.posedge_event());
283 }
284}
285} // namespace pin
286} // namespace obi
static uint64_t uniform()
Definition mt19937_rng.h:60
A template class for an optional input port with optimized binding.
A template class for an optional input port with optimized binding.
initiator socket mixin
void register_nb_transport_bw(std::function< sync_enum_type(transaction_type &, phase_type &, sc_core::sc_time &)> cb)
payload_type * allocate()
get a plain tlm_payload_type without extensions
Definition tlm_mm.h:218
T * get() const noexcept
Return the stored pointer.
TLM2.0 components modeling OBI.
Definition obi_tlm.h:25
priority event queue
Definition peq.h:41
static tlm_mm & get()
accessor function of the singleton
Definition tlm_mm.h:338