MatchLib
AxiSlaveToReg.h
1 /*
2  * Copyright (c) 2017-2020, 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 __AXISLAVETOREG_H__
18 #define __AXISLAVETOREG_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 AxiSlaveToReg : 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 slave<> if_axi_rd;
65  typename axi4_::write::template slave<> 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(AxiSlaveToReg)
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 (!axi_wr_req_data.wstrb.and_reduce()) { // Non-uniform write strobe - need to do read-modify-write
185  NVUINTW(axi4_::DATA_WIDTH) old_data = reg[regAddr];
186 #pragma hls_unroll yes
187  for (int i=0; i<axi4_::WSTRB_WIDTH; i++) {
188  if (axi_wr_req_data.wstrb[i] == 0) {
189  axiData = nvhls::set_slc(axiData, nvhls::get_slc<8>(old_data,8*i), 8*i);
190  }
191  }
192  }
193 #pragma hls_unroll yes
194  for (int i=0; i<numReg; i++) { // More verbose, but this is the preferred coding style for HLS
195  if (i == regAddr) {
196  reg[i] = axiData;
197  }
198  }
199  CDCOUT(sc_time_stamp() << " " << name() << " Wrote to local reg:"
200  << " axi_addr=" << hex << axiWrAddr.to_int64()
201  << " reg_addr=" << regAddr.to_int64()
202  << " data=" << hex << axi_wr_req_data.data
203  << " wstrb=" << hex << axi_wr_req_data.wstrb.to_uint64()
204  << endl, kDebugLevel);
205  if (axi_wr_req_data.last == 1) {
206  write_arb_req = 0;
207  arb_needs_update = 1;
208  if (axiCfg::useWriteResponses) {
209  axi_wr_resp.id = axi_wr_req_addr.id;
210  if (valid_wr_addr) {
211  axi_wr_resp.resp = axi4_::Enc::XRESP::OKAY;
212  } else {
213  axi_wr_resp.resp = axi4_::Enc::XRESP::SLVERR;
214  }
215  if_axi_wr.bwrite(axi_wr_resp);
216  }
217  } else {
218  axiWrAddr += bytesPerReg;
219  }
220  }
221  }
222 #pragma hls_unroll yes
223  for (int i=0; i<numReg; i++) {
224  regOut[i].write(reg[i]);
225  }
226  }
227  }
228 };
229 
230 #endif
An AXI slave containing memory-mapped registers.
Definition: AxiSlaveToReg.h:55
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 an AXI write response.
Definition: axi4.h:236
NVUINTW(Wrapped< T >::width) TypeToNVUINT(T in)
Convert Type to NVUINT.
Definition: TypeToBits.h:115
A struct composed of the signals associated with AXI write data.
Definition: axi4.h:279
A struct composed of the signals associated with AXI read and write requests.
Definition: axi4.h:108
A struct composed of the signals associated with an AXI read response.
Definition: axi4.h:180
#define CDCOUT(x, y)
Definition: hls_globals.h:73
The base axi4 class parameterized according a valid config.
Definition: axi4.h:58
Compute Celing of log2 of a constant.
Definition: nvhls_int.h:174
#define NVHLS_ASSERT_MSG(X, MSG)
Definition: nvhls_assert.h:116
A generalized implementation of generic n-way roundrobin arbiter.
Definition: Arbiter.h:61