MatchLib
All Classes Namespaces Files Functions Modules Pages
AxiSubordinateToReg.h
1/*
2 * Copyright (c) 2017-2024, NVIDIA CORPORATION. All rights reserved.
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 __AXISUBORDINATETOREG_H__
18#define __AXISUBORDINATETOREG_H__
19
20#include <systemc.h>
21#include <ac_int.h>
22#include <hls_globals.h>
23#include <axi/axi4.h>
24#include <mem_array.h>
25#include <fifo.h>
26#include "Arbiter.h"
27
54template <typename axiCfg, int numReg, int numAddrBitsToInspect = axiCfg::addrWidth>
55class AxiSubordinateToReg : public sc_module {
56 public:
57 static const int kDebugLevel = 5;
58
59 typedef typename axi::axi4<axiCfg> axi4_;
60
61 sc_in<bool> clk;
62 sc_in<bool> reset_bar;
63
64 typename axi4_::read::template subordinate<> if_axi_rd;
65 typename axi4_::write::template subordinate<> if_axi_wr;
66
67 static const int regAddrWidth = nvhls::log2_ceil<numReg>::val;
68 static const int bytesPerReg = axi4_::DATA_WIDTH >> 3;
69 static const int axiAddrBitsPerReg = nvhls::log2_ceil<bytesPerReg>::val;
70
71 sc_in<NVUINTW(numAddrBitsToInspect)> baseAddr;
72
73 // Each reg is one AXI data word
74 sc_out<NVUINTW(axi4_::DATA_WIDTH)> regOut[numReg];
75
76 public:
77 SC_CTOR(AxiSubordinateToReg)
78 : clk("clk"),
79 reset_bar("reset_bar"),
80 if_axi_rd("if_axi_rd"),
81 if_axi_wr("if_axi_wr")
82 {
83 SC_THREAD(run);
84 sensitive << clk.pos();
85 async_reset_signal_is(reset_bar, false);
86 }
87
88 protected:
89 void run() {
90 if_axi_rd.reset();
91 if_axi_wr.reset();
92
93 NVUINTW(axi4_::DATA_WIDTH) reg[numReg];
94 NVUINTW(numAddrBitsToInspect) maxValidAddr = baseAddr.read() + bytesPerReg*numReg - 1;
95
96#pragma hls_unroll yes
97 for (int i=0; i<numReg; i++) {
98 reg[i] = 0;
99 regOut[i].write(reg[i]);
100 }
101
102 typename axi4_::AddrPayload axi_rd_req;
103 typename axi4_::ReadPayload axi_rd_resp;
104 typename axi4_::AddrPayload axi_wr_req_addr;
105 typename axi4_::WritePayload axi_wr_req_data;
106 typename axi4_::WRespPayload axi_wr_resp;
107
108 NVUINTW(numAddrBitsToInspect) axiRdAddr;
109 NVUINTW(axi4_::ALEN_WIDTH) axiRdLen;
110 bool valid_rd_addr;
111 NVUINTW(numAddrBitsToInspect) axiWrAddr;
112 bool valid_wr_addr;
113
114 bool read_arb_req = 0;
115 bool write_arb_req = 0;
116 bool arb_needs_update = 1;
117 NVUINTW(2) valid_mask = 0;
118 NVUINTW(2) select_mask = 0;
119 Arbiter<2> arb;
120
121 #pragma hls_pipeline_init_interval 1
122 #pragma pipeline_stall_mode flush
123 while (1) {
124 wait();
125
126 valid_mask = write_arb_req << 1 | read_arb_req;
127 if (arb_needs_update) {
128 select_mask = arb.pick(valid_mask);
129 if (select_mask != 0) arb_needs_update = 0;
130 }
131
132 if (!read_arb_req) {
133 if (if_axi_rd.nb_aread(axi_rd_req)) {
134 read_arb_req = 1;
135 NVUINTW(numAddrBitsToInspect) addr_temp(static_cast<sc_uint<numAddrBitsToInspect> >(axi_rd_req.addr));
136 NVUINTW(axi4_::ALEN_WIDTH) len_temp(static_cast< sc_uint<axi4_::ALEN_WIDTH> >(axi_rd_req.len));
137 axiRdAddr = addr_temp;
138 axiRdLen = len_temp;
139 }
140 }
141
142 if (!write_arb_req) {
143 if (if_axi_wr.aw.PopNB(axi_wr_req_addr)) {
144 write_arb_req = 1;
145 NVUINTW(numAddrBitsToInspect) addr_temp(static_cast< sc_uint<numAddrBitsToInspect> >(axi_wr_req_addr.addr));
146 axiWrAddr = addr_temp;
147 }
148 }
149
150 if (select_mask == 1) {
151 valid_rd_addr = (axiRdAddr >= baseAddr.read() && axiRdAddr <= maxValidAddr);
152 NVHLS_ASSERT_MSG(valid_rd_addr, "Read address is out of bounds");
153 NVUINTW(regAddrWidth) regAddr = (axiRdAddr - baseAddr.read()) >> axiAddrBitsPerReg;
154 axi_rd_resp.id = axi_rd_req.id;
155 if (valid_rd_addr) {
156 axi_rd_resp.resp = axi4_::Enc::XRESP::OKAY;
157 NVUINTW(axi4_::DATA_WIDTH) read_data;
158 axi_rd_resp.data = reg[regAddr];
159 }
160 else {
161 axi_rd_resp.resp = axi4_::Enc::XRESP::SLVERR;
162 }
163 if (axiRdLen == 0) {
164 axi_rd_resp.last = 1;
165 read_arb_req = 0;
166 arb_needs_update = 1;
167 } else {
168 axi_rd_resp.last = 0;
169 axiRdLen--;
170 axiRdAddr += bytesPerReg;
171 }
172 if_axi_rd.rwrite(axi_rd_resp);
173 CDCOUT(sc_time_stamp() << " " << name() << " Read from local reg:"
174 << " axi_addr=" << hex << axiRdAddr.to_int64()
175 << " reg_addr=" << regAddr.to_int64()
176 << " data=" << hex << axi_rd_resp.data
177 << endl, kDebugLevel);
178 } else if (select_mask == 2) {
179 if (if_axi_wr.w.PopNB(axi_wr_req_data)) {
180 valid_wr_addr = (axiWrAddr >= baseAddr.read() && axiWrAddr <= maxValidAddr);
181 NVHLS_ASSERT_MSG(valid_wr_addr, "Write address is out of bounds");
182 NVUINTW(axi4_::DATA_WIDTH) axiData(static_cast<typename axi4_::Data>(axi_wr_req_data.data));
183 NVUINTW(regAddrWidth) regAddr = (axiWrAddr - baseAddr.read()) >> axiAddrBitsPerReg;
184 if (axi4_::WSTRB_WIDTH > 0) {
185 if (!axi_wr_req_data.wstrb.and_reduce()) { // Non-uniform write strobe - need to do read-modify-write
186 NVUINTW(axi4_::DATA_WIDTH) old_data = reg[regAddr];
187#pragma hls_unroll yes
188 for (int i=0; i<axi4_::WSTRB_WIDTH; i++) {
189 if (axi_wr_req_data.wstrb[i] == 0) {
190 axiData = nvhls::set_slc(axiData, nvhls::get_slc<8>(old_data,8*i), 8*i);
191 }
192 }
193 }
194 }
195#pragma hls_unroll yes
196 for (int i=0; i<numReg; i++) { // More verbose, but this is the preferred coding style for HLS
197 if (i == regAddr) {
198 reg[i] = axiData;
199 }
200 }
201 CDCOUT(sc_time_stamp() << " " << name() << " Wrote to local reg:"
202 << " axi_addr=" << hex << axiWrAddr.to_int64()
203 << " reg_addr=" << regAddr.to_int64()
204 << " data=" << hex << axi_wr_req_data.data
205 << " wstrb=" << hex << axi_wr_req_data.wstrb.to_uint64()
206 << endl, kDebugLevel);
207 if (axi_wr_req_data.last == 1) {
208 write_arb_req = 0;
209 arb_needs_update = 1;
210 if (axiCfg::useWriteResponses) {
211 axi_wr_resp.id = axi_wr_req_addr.id;
212 if (valid_wr_addr) {
213 axi_wr_resp.resp = axi4_::Enc::XRESP::OKAY;
214 } else {
215 axi_wr_resp.resp = axi4_::Enc::XRESP::SLVERR;
216 }
217 if_axi_wr.bwrite(axi_wr_resp);
218 }
219 } else {
220 axiWrAddr += bytesPerReg;
221 }
222 }
223 }
224#pragma hls_unroll yes
225 for (int i=0; i<numReg; i++) {
226 regOut[i].write(reg[i]);
227 }
228 }
229 }
230};
231
260template <typename axiCfg, int numControlReg, int numStatusReg, int numAddrBitsToInspect = axiCfg::addrWidth>
261class AxiSubordinateToCSReg : public sc_module {
262 public:
263 static const int kDebugLevel = 5;
264
265 typedef typename axi::axi4<axiCfg> axi4_;
266
267 sc_in<bool> clk;
268 sc_in<bool> reset_bar;
269
270 typename axi4_::read::template subordinate<> if_axi_rd;
271 typename axi4_::write::template subordinate<> if_axi_wr;
272
273 static const int numReg = numControlReg + numStatusReg;
274 static const int controlRegAddrWidth = nvhls::log2_ceil<numControlReg>::val;
275 static const int statusRegAddrWidth = nvhls::log2_ceil<numStatusReg>::val;
276 static const int bytesPerReg = axi4_::DATA_WIDTH >> 3;
277 static const int axiAddrBitsPerReg = nvhls::log2_ceil<bytesPerReg>::val;
278
279 sc_in<NVUINTW(numAddrBitsToInspect)> baseAddr;
280
281 // Each reg is one AXI data word
282 sc_out<NVUINTW(axi4_::DATA_WIDTH)> regOut[numControlReg];
283 sc_in<NVUINTW(axi4_::DATA_WIDTH)> regIn[numStatusReg];
284
285 public:
286 SC_CTOR(AxiSubordinateToCSReg)
287 : clk("clk"),
288 reset_bar("reset_bar"),
289 if_axi_rd("if_axi_rd"),
290 if_axi_wr("if_axi_wr")
291 {
292 SC_THREAD(run);
293 sensitive << clk.pos();
294 async_reset_signal_is(reset_bar, false);
295 }
296
297 protected:
298 void run() {
299 if_axi_rd.reset();
300 if_axi_wr.reset();
301
302 NVUINTW(axi4_::DATA_WIDTH) reg[numControlReg];
303 NVUINTW(numAddrBitsToInspect) maxValidRdAddr = baseAddr.read() + bytesPerReg*numReg - 1;
304 NVUINTW(numAddrBitsToInspect) maxValidWrAddr = baseAddr.read() + bytesPerReg*numControlReg - 1;
305
306#pragma hls_unroll yes
307 for (int i=0; i<numControlReg; i++) {
308 reg[i] = 0;
309 regOut[i].write(reg[i]);
310 }
311
312 typename axi4_::AddrPayload axi_rd_req;
313 typename axi4_::ReadPayload axi_rd_resp;
314 typename axi4_::AddrPayload axi_wr_req_addr;
315 typename axi4_::WritePayload axi_wr_req_data;
316 typename axi4_::WRespPayload axi_wr_resp;
317
318 NVUINTW(numAddrBitsToInspect) axiRdAddr;
319 NVUINTW(axi4_::ALEN_WIDTH) axiRdLen;
320 bool valid_rd_addr;
321 NVUINTW(numAddrBitsToInspect) axiWrAddr;
322 bool valid_wr_addr;
323
324 bool read_arb_req = 0;
325 bool write_arb_req = 0;
326 bool arb_needs_update = 1;
327 NVUINTW(2) valid_mask = 0;
328 NVUINTW(2) select_mask = 0;
329 Arbiter<2> arb;
330
331 #pragma hls_pipeline_init_interval 1
332 #pragma pipeline_stall_mode flush
333 while (1) {
334 wait();
335
336 valid_mask = write_arb_req << 1 | read_arb_req;
337 if (arb_needs_update) {
338 select_mask = arb.pick(valid_mask);
339 if (select_mask != 0) arb_needs_update = 0;
340 }
341
342 if (!read_arb_req) {
343 if (if_axi_rd.nb_aread(axi_rd_req)) {
344 read_arb_req = 1;
345 NVUINTW(numAddrBitsToInspect) addr_temp(static_cast<sc_uint<numAddrBitsToInspect> >(axi_rd_req.addr));
346 NVUINTW(axi4_::ALEN_WIDTH) len_temp(static_cast< sc_uint<axi4_::ALEN_WIDTH> >(axi_rd_req.len));
347 axiRdAddr = addr_temp;
348 axiRdLen = len_temp;
349 }
350 }
351
352 if (!write_arb_req) {
353 if (if_axi_wr.aw.PopNB(axi_wr_req_addr)) {
354 write_arb_req = 1;
355 NVUINTW(numAddrBitsToInspect) addr_temp(static_cast< sc_uint<numAddrBitsToInspect> >(axi_wr_req_addr.addr));
356 axiWrAddr = addr_temp;
357 }
358 }
359
360 if (select_mask == 1) {
361 valid_rd_addr = (axiRdAddr >= baseAddr.read() && axiRdAddr <= maxValidRdAddr);
362 NVHLS_ASSERT_MSG(valid_rd_addr, "Read address is out of bounds");
363 NVUINTW(controlRegAddrWidth) controlRegAddr = (axiRdAddr - baseAddr.read()) >> axiAddrBitsPerReg;
364 NVUINTW(statusRegAddrWidth) statusRegAddr = ((axiRdAddr - baseAddr.read()) >> axiAddrBitsPerReg) - numControlReg;
365 axi_rd_resp.id = axi_rd_req.id;
366 if (valid_rd_addr) {
367 axi_rd_resp.resp = axi4_::Enc::XRESP::OKAY;
368 NVUINTW(axi4_::DATA_WIDTH) read_data;
369 if (axiRdAddr <= maxValidWrAddr) { // Read internal control register
370 axi_rd_resp.data = reg[controlRegAddr];
371 } else { // Read external status register
372 axi_rd_resp.data = regIn[statusRegAddr].read();
373 }
374 }
375 else {
376 axi_rd_resp.resp = axi4_::Enc::XRESP::SLVERR;
377 }
378 if (axiRdLen == 0) {
379 axi_rd_resp.last = 1;
380 read_arb_req = 0;
381 arb_needs_update = 1;
382 } else {
383 axi_rd_resp.last = 0;
384 axiRdLen--;
385 axiRdAddr += bytesPerReg;
386 }
387 if_axi_rd.rwrite(axi_rd_resp);
388 if (axiRdAddr <= maxValidWrAddr) {
389 CDCOUT(sc_time_stamp() << " " << name() << " Read from control reg:"
390 << " axi_addr=" << hex << axiRdAddr.to_int64()
391 << " reg_addr=" << controlRegAddr.to_int64()
392 << " data=" << hex << axi_rd_resp.data
393 << endl, kDebugLevel);
394 } else {
395 CDCOUT(sc_time_stamp() << " " << name() << " Read from status reg:"
396 << " axi_addr=" << hex << axiRdAddr.to_int64()
397 << " reg_addr=" << statusRegAddr.to_int64()
398 << " data=" << hex << axi_rd_resp.data
399 << endl, kDebugLevel);
400 }
401 } else if (select_mask == 2) {
402 if (if_axi_wr.w.PopNB(axi_wr_req_data)) {
403 valid_wr_addr = (axiWrAddr >= baseAddr.read() && axiWrAddr <= maxValidWrAddr);
404 NVHLS_ASSERT_MSG(valid_wr_addr, "Write address is out of bounds");
405 NVUINTW(axi4_::DATA_WIDTH) axiData(static_cast<typename axi4_::Data>(axi_wr_req_data.data));
406 NVUINTW(controlRegAddrWidth) regAddr = (axiWrAddr - baseAddr.read()) >> axiAddrBitsPerReg;
407 if (axi4_::WSTRB_WIDTH > 0) {
408 if (!axi_wr_req_data.wstrb.and_reduce()) { // Non-uniform write strobe - need to do read-modify-write
409 NVUINTW(axi4_::DATA_WIDTH) old_data = reg[regAddr];
410#pragma hls_unroll yes
411 for (int i=0; i<axi4_::WSTRB_WIDTH; i++) {
412 if (axi_wr_req_data.wstrb[i] == 0) {
413 axiData = nvhls::set_slc(axiData, nvhls::get_slc<8>(old_data,8*i), 8*i);
414 }
415 }
416 }
417 }
418#pragma hls_unroll yes
419 for (int i=0; i<numControlReg; i++) { // More verbose, but this is the preferred coding style for HLS
420 if (i == regAddr) {
421 reg[i] = axiData;
422 }
423 }
424 CDCOUT(sc_time_stamp() << " " << name() << " Wrote to local reg:"
425 << " axi_addr=" << hex << axiWrAddr.to_int64()
426 << " reg_addr=" << regAddr.to_int64()
427 << " data=" << hex << axi_wr_req_data.data
428 << " wstrb=" << hex << axi_wr_req_data.wstrb.to_uint64()
429 << endl, kDebugLevel);
430 if (axi_wr_req_data.last == 1) {
431 write_arb_req = 0;
432 arb_needs_update = 1;
433 if (axiCfg::useWriteResponses) {
434 axi_wr_resp.id = axi_wr_req_addr.id;
435 if (valid_wr_addr) {
436 axi_wr_resp.resp = axi4_::Enc::XRESP::OKAY;
437 } else {
438 axi_wr_resp.resp = axi4_::Enc::XRESP::SLVERR;
439 }
440 if_axi_wr.bwrite(axi_wr_resp);
441 }
442 } else {
443 axiWrAddr += bytesPerReg;
444 }
445 }
446 }
447#pragma hls_unroll yes
448 for (int i=0; i<numControlReg; i++) {
449 regOut[i].write(reg[i]);
450 }
451 }
452 }
453};
454
455#endif
A generalized implementation of generic n-way roundrobin arbiter.
Definition Arbiter.h:61
An AXI subordinate containing memory-mapped registers.
An AXI subordinate containing memory-mapped registers.
The base axi4 class parameterized according a valid config.
Definition axi4.h:64
#define NVHLS_ASSERT_MSG(X, MSG)
#define CDCOUT(x, y)
Definition hls_globals.h:73
#define NVUINTW(width)
Definition nvhls_types.h:35
nvhls_t< W >::nvuint_t get_slc(type X, const unsigned int i)
Function that returns slice of bits.
Definition nvhls_int.h:437
type1 set_slc(type1 X, type2 Y, const unsigned int i)
Function that replaces slice of bits.
Definition nvhls_int.h:387
A struct composed of the signals associated with AXI read and write requests.
Definition axi4.h:114
A struct composed of the signals associated with an AXI read response.
Definition axi4.h:157
A struct composed of the signals associated with an AXI write response.
Definition axi4.h:190
A struct composed of the signals associated with AXI write data.
Definition axi4.h:215
Compute Celing of log2 of a constant.
Definition nvhls_int.h:174