Fletchgen
The Fletcher Design Generator
sim.cc
1 // Copyright 2018-2019 Delft University of Technology
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "fletchgen/top/sim.h"
16 
17 #include <fletcher/fletcher.h>
18 #include <cerata/api.h>
19 #include <cerata/vhdl/vhdl.h>
20 #include <string>
21 #include <iomanip>
22 #include <cstdlib>
23 
24 #include "fletchgen/top/sim_template.h"
25 #include "fletchgen/mantle.h"
26 
27 namespace fletchgen::top {
28 
29 using fletcher::RecordBatchDescription;
30 using cerata::vhdl::Template;
31 
32 static std::string GenMMIOWrite(uint32_t idx, uint32_t value, const std::string &comment = "") {
33  std::stringstream str;
34  str << " mmio_write32("
35  << std::dec << idx << ", "
36  << "X\"" << std::setfill('0') << std::setw(8) << std::hex << value << "\","
37  << " mmio_source, mmio_sink, bcd_clk, bcd_reset);";
38  if (!comment.empty()) {
39  str << " -- " << comment;
40  }
41  str << std::endl;
42  return str.str();
43 }
44 
45 static std::string GenMMIORead(uint32_t idx,
46  const std::string &print_prefix,
47  bool hex_ndec = true,
48  const std::string &comment = "") {
49  std::stringstream str;
50  str << " mmio_read32("
51  << std::dec << idx << ", "
52  << " read_data, "
53  << " mmio_source, mmio_sink, bcd_clk, bcd_reset);";
54  if (!comment.empty()) {
55  str << " -- " << comment;
56  }
57  str << "\n";
58  str << R"( println(")" + print_prefix;
59  if (hex_ndec) {
60  str << R"(: " & slvToHex(read_data));)" << std::endl;
61  } else {
62  str << R"(: " & slvToDec(read_data));)" << std::endl;
63  }
64  return str.str();
65 }
66 
67 static std::string CanonicalizePath(const std::string &path) {
68  std::string result;
69  if (!path.empty()) {
70  char *p = realpath(path.c_str(), nullptr);
71  if (p == nullptr) {
72  FLETCHER_LOG(FATAL, "Could not canonicalize path: " << path);
73  }
74  result = std::string(p);
75  free(p);
76  }
77  return result;
78 }
79 
80 std::string GenerateSimTop(const Design &design,
81  const std::vector<std::ostream *> &outputs,
82  const std::string &read_srec_path,
83  const std::string &write_srec_path,
84  const std::vector<RecordBatchDescription> &recordbatches) {
85  // Template file for simulation top-level
86  auto t = Template::FromString(sim_source);
87 
88  // Offset of schema specific registers
89  constexpr int ndefault = FLETCHER_REG_SCHEMA;
90 
91  // Obtain read/write schemas.
92  auto read_schemas = design.schema_set->read_schemas();
93  auto write_schemas = design.schema_set->write_schemas();
94 
95  // Total number of RecordBatches
96  size_t num_rbs = read_schemas.size() + write_schemas.size();
97 
98  // Bus properties
99  t.Replace("BUS_ADDR_WIDTH", 64);
100  t.Replace("BUS_DATA_WIDTH", 512);
101  t.Replace("BUS_LEN_WIDTH", 8);
102  t.Replace("BUS_BURST_STEP_LEN", 1);
103  t.Replace("BUS_BURST_MAX_LEN", 64);
104 
105  t.Replace("MMIO_DATA_WIDTH", design.mmio_spec.data_width);
106  t.Replace("MMIO_ADDR_WIDTH", design.mmio_spec.addr_width);
107  t.Replace("MMIO_STRB", design.mmio_spec.data_width == 64 ? R"(X"0F" when idx mod 2 = 0 else X"F0")" : R"(X"F")");
108  t.Replace("MMIO_RW_DATA_RANGE",
109  design.mmio_spec.data_width == 64 ? R"((32 * (1 + idx mod 2)-1 downto 32 * (idx mod 2)))" : "");
110  t.Replace("MMIO_OFFSET", design.mmio_spec.offset);
111 
112  // Do not change this order, TODO: fix this in replacement code
113  t.Replace("FLETCHER_WRAPPER_NAME", design.mantle_comp->name());
114  t.Replace("FLETCHER_WRAPPER_INST_NAME", design.mantle_comp->name() + "_inst");
115 
116  t.Replace("READ_SREC_PATH", read_srec_path);
117  t.Replace("WRITE_SREC_PATH", write_srec_path);
118 
119  // Mantle declaration
120  t.Replace("MANTLE_DECL", cerata::vhdl::Decl::Generate(*design.mantle_comp, false, 1).ToString());
121 
122  // Generate all the buffer and recordbatch metadata
123  std::stringstream buffer_meta;
124  std::stringstream rb_meta;
125 
126  FLETCHER_LOG(DEBUG, "SIM: Generating MMIO writes for " << num_rbs << " RecordBatches.");
127 
128  // Loop over all RecordBatches
129  size_t buffer_offset = 0;
130  size_t rb_offset = 0;
131  for (const auto &rb : recordbatches) {
132  for (const auto &f : rb.fields) {
133  for (const auto &b : f.buffers) {
134  // Get the low and high part of the address
135  auto addr = reinterpret_cast<uint64_t>(b.raw_buffer_);
136  auto addr_lo = (uint32_t) (addr & 0xFFFFFFFF);
137  auto addr_hi = (uint32_t) (addr >> 32u);
138  uint32_t buffer_idx = 2 * (buffer_offset) + (ndefault + 2 * num_rbs);
139  buffer_meta << GenMMIOWrite(buffer_idx,
140  addr_lo,
141  rb.name + " " + fletcher::ToString(b.desc_) + " buffer address.");
142  buffer_meta << GenMMIOWrite(buffer_idx + 1,
143  addr_hi);
144  buffer_offset++;
145  }
146  }
147  uint32_t rb_idx = 2 * (rb_offset) + ndefault;
148  rb_meta << GenMMIOWrite(rb_idx, 0, rb.name + " first index.");
149  rb_meta << GenMMIOWrite(rb_idx + 1, rb.rows, rb.name + " last index.");
150  rb_offset++;
151  }
152  t.Replace("SREC_BUFFER_ADDRESSES", buffer_meta.str());
153  t.Replace("SREC_FIRSTLAST_INDICES", rb_meta.str());
154 
155  std::stringstream kri;
156  if (!design.kernel_regs.empty()) {
157  for (const auto &cr : design.kernel_regs) {
158  if (cr.behavior == MmioBehavior::CONTROL) {
159  // TODO(johanpel): fix this for non-32 bit regs
160  auto val = cr.init ? *cr.init : 0;
161  kri << GenMMIOWrite(cr.addr.value() / 4, val, "Write register \"" + cr.name + "\" initial value.");
162  }
163  }
164  }
165  t.Replace("KERNEL_REGS_INIT", kri.str());
166 
167  // Profiling registers.
168  if (!design.profiling_regs.empty()) {
169  std::stringstream profile_reads;
170  uint32_t addr = 0;
171  int i = 0;
172  for (const auto &pr : design.profiling_regs) {
173  if (pr.name == "Profile_enable") {
174  // Profiling register should have an address now.
175  addr = pr.addr.value();
176  } else if (pr.name == "Profile_clear") {
177  // do nothing
178  } else if (pr.function == MmioFunction::PROFILE) {
179  i++;
180  std::stringstream profile_prefix;
181  profile_prefix << std::setw(42) << ("Profile " + pr.name);
182  profile_reads << GenMMIORead(pr.addr.value() / 4, profile_prefix.str(), false);
183  }
184  }
185  t.Replace("PROFILE_START", GenMMIOWrite(addr / 4, 1, "Start profiling."));
186  t.Replace("PROFILE_STOP", GenMMIOWrite(addr / 4, 0, "Stop profiling."));
187  t.Replace("PROFILE_READ", profile_reads.str());
188  } else {
189  t.Replace("PROFILE_START", "");
190  t.Replace("PROFILE_STOP", "");
191  t.Replace("PROFILE_READ", "");
192  }
193 
194  // Read/write specific memory models
195  if (design.schema_set->RequiresReading()) {
196  auto abs_path = CanonicalizePath(read_srec_path);
197  t.Replace("BUS_READ_SLAVE_MOCK",
198  " rmem_inst: BusReadSlaveMock\n"
199  " generic map (\n"
200  " BUS_ADDR_WIDTH => BUS_ADDR_WIDTH,\n"
201  " BUS_LEN_WIDTH => BUS_LEN_WIDTH,\n"
202  " BUS_DATA_WIDTH => BUS_DATA_WIDTH,\n"
203  " SEED => 1337,\n"
204  " RANDOM_REQUEST_TIMING => false,\n"
205  " RANDOM_RESPONSE_TIMING => false,\n"
206  " SREC_FILE => \"" +
207  abs_path
208  + "\"\n"
209  " )\n"
210  " port map (\n"
211  " clk => bcd_clk,\n"
212  " reset => bcd_reset,\n"
213  " rreq_valid => bus_rreq_valid,\n"
214  " rreq_ready => bus_rreq_ready,\n"
215  " rreq_addr => bus_rreq_addr,\n"
216  " rreq_len => bus_rreq_len,\n"
217  " rdat_valid => bus_rdat_valid,\n"
218  " rdat_ready => bus_rdat_ready,\n"
219  " rdat_data => bus_rdat_data,\n"
220  " rdat_last => bus_rdat_last\n"
221  " );\n"
222  "\n");
223 
224  t.Replace("MST_RREQ_DECLARE",
225  " rd_mst_rreq_valid : out std_logic;\n"
226  " rd_mst_rreq_ready : in std_logic;\n"
227  " rd_mst_rreq_addr : out std_logic_vector(BUS_ADDR_WIDTH-1 downto 0);\n"
228  " rd_mst_rreq_len : out std_logic_vector(BUS_LEN_WIDTH-1 downto 0);\n"
229  " rd_mst_rdat_valid : in std_logic;\n"
230  " rd_mst_rdat_ready : out std_logic;\n"
231  " rd_mst_rdat_data : in std_logic_vector(BUS_DATA_WIDTH-1 downto 0);\n"
232  " rd_mst_rdat_last : in std_logic;\n");
233 
234  t.Replace("MST_RREQ_INSTANTIATE",
235  " rd_mst_rreq_valid => bus_rreq_valid,\n"
236  " rd_mst_rreq_ready => bus_rreq_ready,\n"
237  " rd_mst_rreq_addr => bus_rreq_addr,\n"
238  " rd_mst_rreq_len => bus_rreq_len,\n"
239  " rd_mst_rdat_valid => bus_rdat_valid,\n"
240  " rd_mst_rdat_ready => bus_rdat_ready,\n"
241  " rd_mst_rdat_data => bus_rdat_data,\n"
242  " rd_mst_rdat_last => bus_rdat_last,\n");
243  } else {
244  t.Replace("BUS_READ_SLAVE_MOCK", "");
245  t.Replace("MST_RREQ_DECLARE", "");
246  t.Replace("MST_RREQ_INSTANTIATE", "");
247  }
248  if (design.schema_set->RequiresWriting()) {
249  t.Replace("BUS_WRITE_SLAVE_MOCK",
250  " wmem_inst: BusWriteSlaveMock\n"
251  " generic map (\n"
252  " BUS_ADDR_WIDTH => BUS_ADDR_WIDTH,\n"
253  " BUS_LEN_WIDTH => BUS_LEN_WIDTH,\n"
254  " BUS_DATA_WIDTH => BUS_DATA_WIDTH,\n"
255  " SEED => 1337,\n"
256  " RANDOM_REQUEST_TIMING => false,\n"
257  " RANDOM_RESPONSE_TIMING => false,\n"
258  " SREC_FILE => \""
259  + CanonicalizePath(write_srec_path)
260  + "\"\n"
261  " )\n"
262  " port map (\n"
263  " clk => bcd_clk,\n"
264  " reset => bcd_reset,\n"
265  " wreq_valid => bus_wreq_valid,\n"
266  " wreq_ready => bus_wreq_ready,\n"
267  " wreq_addr => bus_wreq_addr,\n"
268  " wreq_len => bus_wreq_len,\n"
269  " wreq_last => bus_wreq_last,\n"
270  " wdat_valid => bus_wdat_valid,\n"
271  " wdat_ready => bus_wdat_ready,\n"
272  " wdat_data => bus_wdat_data,\n"
273  " wdat_strobe => bus_wdat_strobe,\n"
274  " wdat_last => bus_wdat_last,\n"
275  " wrep_valid => bus_wrep_valid,\n"
276  " wrep_ready => bus_wrep_ready,\n"
277  " wrep_ok => bus_wrep_ok\n"
278  " );");
279 
280  t.Replace("MST_WREQ_DECLARE",
281  " wr_mst_wreq_valid : out std_logic;\n"
282  " wr_mst_wreq_ready : in std_logic;\n"
283  " wr_mst_wreq_addr : out std_logic_vector(BUS_ADDR_WIDTH-1 downto 0);\n"
284  " wr_mst_wreq_len : out std_logic_vector(BUS_LEN_WIDTH-1 downto 0);\n"
285  " wr_mst_wreq_last : out std_logic;\n"
286  " wr_mst_wdat_valid : out std_logic;\n"
287  " wr_mst_wdat_ready : in std_logic;\n"
288  " wr_mst_wdat_data : out std_logic_vector(BUS_DATA_WIDTH-1 downto 0);\n"
289  " wr_mst_wdat_strobe : out std_logic_vector(BUS_DATA_WIDTH/8-1 downto 0);\n"
290  " wr_mst_wdat_last : out std_logic;"
291  " wr_mst_wrep_valid : in std_logic;\n"
292  " wr_mst_wrep_ready : out std_logic;\n"
293  " wr_mst_wrep_ok : in std_logic;\n");
294 
295  t.Replace("MST_WREQ_INSTANTIATE",
296  " wr_mst_wreq_valid => bus_wreq_valid,\n"
297  " wr_mst_wreq_ready => bus_wreq_ready,\n"
298  " wr_mst_wreq_addr => bus_wreq_addr,\n"
299  " wr_mst_wreq_len => bus_wreq_len,\n"
300  " wr_mst_wreq_last => bus_wreq_last,\n"
301  " wr_mst_wdat_valid => bus_wdat_valid,\n"
302  " wr_mst_wdat_ready => bus_wdat_ready,\n"
303  " wr_mst_wdat_data => bus_wdat_data,\n"
304  " wr_mst_wdat_strobe => bus_wdat_strobe,\n"
305  " wr_mst_wdat_last => bus_wdat_last,\n"
306  " wr_mst_wrep_valid => bus_wrep_valid,\n"
307  " wr_mst_wrep_ready => bus_wrep_ready,\n"
308  " wr_mst_wrep_ok => bus_wrep_ok,");
309  } else {
310  t.Replace("BUS_WRITE_SLAVE_MOCK", "");
311  t.Replace("MST_WREQ_DECLARE", "");
312  t.Replace("MST_WREQ_INSTANTIATE", "");
313  }
314 
315  if (design.external) {
316  auto ext = design.external.value();
317  auto p_mantle = cerata::port("ext", ext, cerata::Port::Dir::IN);
318  auto s_top = cerata::signal("ext", ext);
319  cerata::Connect(p_mantle, s_top);
320 
321  auto inst_block = cerata::vhdl::Inst::GeneratePortMaps(*p_mantle);
322  inst_block.indent = 3;
323  inst_block << ",";
324 
325  auto decl_block = cerata::vhdl::Decl::Generate(*s_top, 1);
326 
327  t.Replace("EXTERNAL_SIG_DECL", decl_block.ToString());
328  t.Replace("EXTERNAL_INST_MAP", inst_block.ToString());
329  } else {
330  t.Replace("EXTERNAL_SIG_DECL", "");
331  t.Replace("EXTERNAL_INST_MAP", "");
332  }
333 
334  for (auto &o : outputs) {
335  o->flush();
336  *o << t.ToString();
337  }
338 
339  return t.ToString();
340 }
341 
342 } // namespace fletchgen::top
std::shared_ptr< Type > cr()
Fletcher clock/reset;.
Definition: basic_types.cc:73
@ CONTROL
Register contents is controlled by host software.
@ PROFILE
Register for the profiler.