15 #include "fletchgen/top/sim.h"
17 #include <fletcher/fletcher.h>
18 #include <cerata/api.h>
19 #include <cerata/vhdl/vhdl.h>
24 #include "fletchgen/top/sim_template.h"
25 #include "fletchgen/mantle.h"
27 namespace fletchgen::top {
29 using fletcher::RecordBatchDescription;
30 using cerata::vhdl::Template;
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;
45 static std::string GenMMIORead(uint32_t idx,
46 const std::string &print_prefix,
48 const std::string &comment =
"") {
49 std::stringstream str;
50 str <<
" mmio_read32("
51 << std::dec << idx <<
", "
53 <<
" mmio_source, mmio_sink, bcd_clk, bcd_reset);";
54 if (!comment.empty()) {
55 str <<
" -- " << comment;
58 str << R
"( println(")" + print_prefix;
60 str << R
"(: " & slvToHex(read_data));)" << std::endl;
62 str << R
"(: " & slvToDec(read_data));)" << std::endl;
67 static std::string CanonicalizePath(
const std::string &path) {
70 char *p = realpath(path.c_str(),
nullptr);
72 FLETCHER_LOG(FATAL,
"Could not canonicalize path: " << path);
74 result = std::string(p);
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) {
86 auto t = Template::FromString(sim_source);
89 constexpr
int ndefault = FLETCHER_REG_SCHEMA;
92 auto read_schemas = design.schema_set->read_schemas();
93 auto write_schemas = design.schema_set->write_schemas();
96 size_t num_rbs = read_schemas.size() + write_schemas.size();
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);
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);
113 t.Replace(
"FLETCHER_WRAPPER_NAME", design.mantle_comp->name());
114 t.Replace(
"FLETCHER_WRAPPER_INST_NAME", design.mantle_comp->name() +
"_inst");
116 t.Replace(
"READ_SREC_PATH", read_srec_path);
117 t.Replace(
"WRITE_SREC_PATH", write_srec_path);
120 t.Replace(
"MANTLE_DECL", cerata::vhdl::Decl::Generate(*design.mantle_comp,
false, 1).ToString());
123 std::stringstream buffer_meta;
124 std::stringstream rb_meta;
126 FLETCHER_LOG(DEBUG,
"SIM: Generating MMIO writes for " << num_rbs <<
" 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) {
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,
141 rb.name +
" " + fletcher::ToString(b.desc_) +
" buffer address.");
142 buffer_meta << GenMMIOWrite(buffer_idx + 1,
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.");
152 t.Replace(
"SREC_BUFFER_ADDRESSES", buffer_meta.str());
153 t.Replace(
"SREC_FIRSTLAST_INDICES", rb_meta.str());
155 std::stringstream kri;
156 if (!design.kernel_regs.empty()) {
157 for (
const auto &
cr : design.kernel_regs) {
160 auto val =
cr.init ? *
cr.init : 0;
161 kri << GenMMIOWrite(
cr.addr.value() / 4, val,
"Write register \"" +
cr.name +
"\" initial value.");
165 t.Replace(
"KERNEL_REGS_INIT", kri.str());
168 if (!design.profiling_regs.empty()) {
169 std::stringstream profile_reads;
172 for (
const auto &pr : design.profiling_regs) {
173 if (pr.name ==
"Profile_enable") {
175 addr = pr.addr.value();
176 }
else if (pr.name ==
"Profile_clear") {
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);
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());
189 t.Replace(
"PROFILE_START",
"");
190 t.Replace(
"PROFILE_STOP",
"");
191 t.Replace(
"PROFILE_READ",
"");
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"
200 " BUS_ADDR_WIDTH => BUS_ADDR_WIDTH,\n"
201 " BUS_LEN_WIDTH => BUS_LEN_WIDTH,\n"
202 " BUS_DATA_WIDTH => BUS_DATA_WIDTH,\n"
204 " RANDOM_REQUEST_TIMING => false,\n"
205 " RANDOM_RESPONSE_TIMING => false,\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"
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");
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");
244 t.Replace(
"BUS_READ_SLAVE_MOCK",
"");
245 t.Replace(
"MST_RREQ_DECLARE",
"");
246 t.Replace(
"MST_RREQ_INSTANTIATE",
"");
248 if (design.schema_set->RequiresWriting()) {
249 t.Replace(
"BUS_WRITE_SLAVE_MOCK",
250 " wmem_inst: BusWriteSlaveMock\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"
256 " RANDOM_REQUEST_TIMING => false,\n"
257 " RANDOM_RESPONSE_TIMING => false,\n"
259 + CanonicalizePath(write_srec_path)
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"
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");
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,");
310 t.Replace(
"BUS_WRITE_SLAVE_MOCK",
"");
311 t.Replace(
"MST_WREQ_DECLARE",
"");
312 t.Replace(
"MST_WREQ_INSTANTIATE",
"");
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);
321 auto inst_block = cerata::vhdl::Inst::GeneratePortMaps(*p_mantle);
322 inst_block.indent = 3;
325 auto decl_block = cerata::vhdl::Decl::Generate(*s_top, 1);
327 t.Replace(
"EXTERNAL_SIG_DECL", decl_block.ToString());
328 t.Replace(
"EXTERNAL_INST_MAP", inst_block.ToString());
330 t.Replace(
"EXTERNAL_SIG_DECL",
"");
331 t.Replace(
"EXTERNAL_INST_MAP",
"");
334 for (
auto &o : outputs) {
std::shared_ptr< Type > cr()
Fletcher clock/reset;.
@ CONTROL
Register contents is controlled by host software.
@ PROFILE
Register for the profiler.