MatchLib
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 
54 template <typename axiCfg, int numReg, int numAddrBitsToInspect = axiCfg::addrWidth>
55 class 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 
260 template <typename axiCfg, int numControlReg, int numStatusReg, int numAddrBitsToInspect = axiCfg::addrWidth>
261 class 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)
Definition: nvhls_assert.h:135
#define CDCOUT(x, y)
Definition: hls_globals.h:73
NVUINTW(Wrapped< T >::width) TypeToNVUINT(T in)
Convert Type to NVUINT.
Definition: TypeToBits.h:115
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