scc 2025.09
SystemC components library
target.h
1/*******************************************************************************
2 * Copyright 2019-2024 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#ifndef _BUS_APB_PIN_TARGET_H_
18#define _BUS_APB_PIN_TARGET_H_
19
20#include <apb/apb_tlm.h>
21#include <cci_configuration>
22#include <scc/peq.h>
23#include <scc/report.h>
24#include <scc/signal_opt_ports.h>
25#include <scc/utilities.h>
26#include <tlm/scc/initiator_mixin.h>
27#include <tlm/scc/tlm_mm.h>
28#include <tlm>
29#include <tlm_utils/peq_with_get.h>
30
32namespace apb {
34namespace pin {
35
36template <unsigned DATA_WIDTH, unsigned ADDR_WIDTH> class target : sc_core::sc_module {
37 static constexpr bool is_larger(unsigned x) { return x > 64U; }
38
39public:
40 using addr_t = typename std::conditional<is_larger(ADDR_WIDTH), sc_dt::sc_biguint<ADDR_WIDTH>, sc_dt::sc_uint<ADDR_WIDTH>>::type;
41 using data_t = typename std::conditional<is_larger(DATA_WIDTH), sc_dt::sc_biguint<DATA_WIDTH>, sc_dt::sc_uint<DATA_WIDTH>>::type;
42 using strb_t = sc_dt::sc_uint<DATA_WIDTH / 8>;
43
44 sc_core::sc_in<bool> PCLK_i{"PCLK_i"};
45 sc_core::sc_in<bool> PRESETn_i{"PRESETn_i"};
46 sc_core::sc_in<addr_t> PADDR_i{"PADDR_i"};
47 scc::sc_in_opt<sc_dt::sc_uint<3>> PPROT_i{"PPROT_i"};
48 scc::sc_in_opt<bool> PNSE_i{"PNSE_i"};
49 sc_core::sc_in<bool> PSELx_i{"PSELx_i"};
50 sc_core::sc_in<bool> PENABLE_i{"PENABLE_i"};
51 sc_core::sc_in<bool> PWRITE_i{"PWRITE_i"};
52 sc_core::sc_in<data_t> PWDATA_i{"PWDATA_i"};
53 scc::sc_in_opt<strb_t> PSTRB_i{"PSTRB_i"};
54 sc_core::sc_out<bool> PREADY_o{"PREADY_o"};
55 sc_core::sc_out<data_t> PRDATA_o{"PRDATA_o"};
56 sc_core::sc_out<bool> PSLVERR_o{"PSLVERR_o"};
57 scc::sc_in_opt<bool> PWAKEUP_i{"PWAKEUP_i"};
58
60
61 cci::cci_param<bool> use_byte_enable{"", false, "sample PSTRB as byte enable in TLM payload"};
62
63 target(const sc_core::sc_module_name& nm);
64
65 virtual ~target();
66
67private:
68 void bus_task();
69 static tlm::tlm_generic_payload* wait4tx(tlm_utils::peq_with_get<tlm::tlm_generic_payload>& que) {
70 tlm::tlm_generic_payload* ret = que.get_next_transaction();
71 while(!ret) {
72 ::sc_core::wait(que.get_event());
73 ret = que.get_next_transaction();
74 }
75 return ret;
76 }
77 sc_core::sc_event end_req_evt;
78 tlm_utils::peq_with_get<tlm::tlm_generic_payload> resp_que{"resp_que"};
79 bool waiting4end_req{false};
80};
81
83// implementations of functions
85template <unsigned DATA_WIDTH, unsigned ADDR_WIDTH>
86target<DATA_WIDTH, ADDR_WIDTH>::target(const sc_core::sc_module_name& nm)
87: sc_module(nm) {
88 SC_HAS_PROCESS(target);
89 SC_THREAD(bus_task);
90 isckt.register_nb_transport_bw([this](tlm::tlm_generic_payload& trans, tlm::tlm_phase& phase, sc_core::sc_time& delay) {
91 if(phase == tlm::END_REQ) {
92 end_req_evt.notify(delay);
93 waiting4end_req = false;
94 } else if(phase == tlm::BEGIN_RESP) {
95 if(waiting4end_req) {
96 end_req_evt.notify(delay);
97 waiting4end_req = false;
98 }
99 resp_que.notify(trans, delay);
100 }
101 return tlm::TLM_ACCEPTED;
102 });
103}
104
105template <unsigned DATA_WIDTH, unsigned ADDR_WIDTH> target<DATA_WIDTH, ADDR_WIDTH>::~target() = default;
106
107template <unsigned DATA_WIDTH, unsigned ADDR_WIDTH> void target<DATA_WIDTH, ADDR_WIDTH>::bus_task() {
108 wait(sc_core::SC_ZERO_TIME);
109 wait(PCLK_i.posedge_event());
110 while(true) {
111 if(!PRESETn_i.read()) {
112 wait(PRESETn_i.posedge_event());
113 wait(PCLK_i.posedge_event());
114 } else {
115 PREADY_o.write(false);
116 wait(sc_core::SC_ZERO_TIME);
117 while(!PSELx_i.read())
118 wait(PSELx_i.value_changed_event());
119 SCCDEBUG(SCMOD) << "Starting APB setup phase";
120 unsigned length = DATA_WIDTH / 8;
121 auto trans = tlm::scc::tlm_mm<>::get().allocate<apb::apb_extension>(length);
122 tlm::scc::tlm_gp_mm::add_data_ptr(length, trans, true);
123 trans->acquire();
124 trans->set_streaming_width(length);
125 trans->set_address(PADDR_i.read());
126 auto* ext = trans->get_extension<apb_extension>();
127 if(PPROT_i.get_interface())
128 ext->set_protection(PPROT_i.read().to_uint());
129 if(PNSE_i.get_interface())
130 ext->set_nse(PNSE_i.read());
131 if(PWRITE_i.read()) {
132 trans->set_write();
133 auto data = PWDATA_i.read();
134 if(use_byte_enable.get_value()) {
135 if(PSTRB_i.get_interface()) {
136 auto strb = PSTRB_i.read();
137 // Copy all data bytes and use byte enables for sparse strobes
138 for(size_t j = 0; j < DATA_WIDTH / 8; ++j) {
139 *(trans->get_data_ptr() + j) = data(8 * j + 7, 8 * j).to_uint();
140 *(trans->get_byte_enable_ptr() + j) = strb[j] ? 0xFF : 0x00;
141 }
142 trans->set_byte_enable_length(DATA_WIDTH / 8);
143 } else {
144 for(size_t j = 0; j < DATA_WIDTH / 8; ++j)
145 *(trans->get_data_ptr() + j) = data(8 * j + 7, 8 * j).to_uint();
146 trans->set_byte_enable_length(0);
147 }
148 } else {
149 auto start_offs = trans->get_address() & (length - 1);
150 if(PSTRB_i.get_interface()) {
151 auto strb = PSTRB_i.read();
152 auto dptr_begin = std::numeric_limits<unsigned>::max();
153 auto dptr_end = 0;
154 for(size_t j = 0; j < DATA_WIDTH / 8; ++j) {
155 if(strb[j]) {
156 if(j < dptr_begin)
157 dptr_begin = j;
158 *(trans->get_data_ptr() + dptr_end) = data(8 * j + 7, 8 * j).to_uint();
159 dptr_end++;
160 }
161 }
162 trans->set_address((trans->get_address() & ~(DATA_WIDTH / 8 - 1)) + dptr_begin);
163 trans->set_data_length(dptr_end);
164 } else
165 for(size_t j = 0; j < DATA_WIDTH / 8; ++j)
166 *(trans->get_data_ptr() + j) = data(8 * j + 7, 8 * j).to_uint();
167 }
168 } else {
169 trans->set_read();
170 }
171 sc_core::sc_time delay;
172 tlm::tlm_phase phase{tlm::BEGIN_REQ};
173 SCCDEBUG(SCMOD) << "Recv beg req for read to addr 0x" << std::hex << trans->get_address();
174 auto res = isckt->nb_transport_fw(*trans, phase, delay);
175 if(res == tlm::TLM_ACCEPTED) {
176 waiting4end_req = true;
177 wait(end_req_evt);
178 phase = tlm::END_REQ;
179 }
180 SCCDEBUG(SCMOD) << "Recv end req for " << (trans->is_write() ? "write to" : "read from") << " addr 0x" << std::hex
181 << trans->get_address();
182 SCCDEBUG(SCMOD) << "APB setup phase, finished";
183 wait(PENABLE_i.posedge_event());
184 if(phase != tlm::BEGIN_RESP) {
185 auto resp = wait4tx(resp_que);
186 sc_assert(trans == resp);
187 }
188 SCCDEBUG(SCMOD) << "Recv beg resp for " << (trans->is_write() ? "write to" : "read from") << " addr 0x" << std::hex
189 << trans->get_address() << ", starting access phase";
190 delay = sc_core::SC_ZERO_TIME;
191 phase = tlm::END_RESP;
192 isckt->nb_transport_fw(*trans, phase, delay);
193 if(trans->is_read()) {
194 data_t data{0};
195 for(size_t j = 0; j < DATA_WIDTH / 8; ++j)
196 data.range(j * 8 + 7, j * 8) = *(trans->get_data_ptr() + j);
197 PRDATA_o.write(data);
198 }
199 PREADY_o.write(true);
200 PSLVERR_o.write(trans->get_response_status() != tlm::TLM_OK_RESPONSE);
201 wait(PCLK_i.posedge_event());
202 SCCDEBUG(SCMOD) << "APB access phase finished";
203 }
204 }
205}
206} // namespace pin
207} /* namespace apb */
208
209#endif /* _BUS_APB_PIN_TARGET_H_ */
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
pin level adapters
Definition initiator.h:34
TLM2.0 components modeling APB.
Definition apb_tlm.cpp:21
static tlm_mm & get()
accessor function of the singleton
Definition tlm_mm.h:338