Skip to content

Commit fe4b912

Browse files
authored
Merge pull request #241 from abs-tudelft/mmio-widths
Add 64-bit data bus option for AXI4-lite MMIO
2 parents 1d9cd0c + 0466afa commit fe4b912

22 files changed

+172
-118
lines changed

codegen/cpp/fletchgen/src/fletchgen/axi4_lite.h

+12-4
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,27 @@ using cerata::ClockDomain;
2525
using cerata::Type;
2626
using cerata::Port;
2727

28-
/// @brief AXI-lite bus width specification. Address is always 32, but data can also be 64. Modifiable.
28+
/// @brief AXI4-lite bus specification.
2929
struct Axi4LiteSpec {
30-
/// The MMIO bus data width.
30+
explicit Axi4LiteSpec(size_t data_width = 32, size_t addr_width = 32, size_t offset = 0)
31+
: data_width(data_width), addr_width(addr_width), offset(offset) {
32+
if (offset % (data_width / 8) != 0) {
33+
throw std::runtime_error("Offset must be integer multiple of data width / 8.");
34+
}
35+
}
36+
/// The data width.
3137
size_t data_width = 32;
32-
/// The MMIO bus address width.
38+
/// The address width.
3339
size_t addr_width = 32;
40+
/// The offset for all registers.
41+
size_t offset = 0;
3442
/// @brief Return a human-readable representation of this Axi4LiteSpec.
3543
[[nodiscard]] std::string ToString() const;
3644
/// @brief Return a Cerata type name based on this Axi4LiteSpec.
3745
[[nodiscard]] std::string ToAxiTypeName() const;
3846
};
3947

40-
/// An AXI4-lite port derived from an AXI-lite width specification.
48+
/// An AXI4-lite port derived from an AXI4-lite specification.
4149
struct Axi4LitePort : public Port {
4250
/// The specification this port was derived from.
4351
Axi4LiteSpec spec_;

codegen/cpp/fletchgen/src/fletchgen/design.cc

+11-5
Original file line numberDiff line numberDiff line change
@@ -186,25 +186,31 @@ Design::Design(const std::shared_ptr<Options> &opts) {
186186
kernel_regs = ParseCustomRegs(opts->regs);
187187
profiling_regs = GetProfilingRegs(recordbatch_comps);
188188

189+
// Parse the memory bus specification.
189190
auto bus_spec = BusDim::FromString(opts->bus_dims[0], BusDim());
190191

192+
// Determine width of the AXI4-lite MMIO.
193+
mmio_spec = Axi4LiteSpec(opts->mmio64 ? 64 : 32, opts->mmio_addr_width, opts->mmio_offset);
194+
191195
// Generate the MMIO component.
192-
mmio_comp = mmio(batch_desc, cerata::Merge({default_regs, recordbatch_regs, kernel_regs, profiling_regs}));
196+
mmio_comp =
197+
mmio(batch_desc, cerata::Merge({default_regs, recordbatch_regs, kernel_regs, profiling_regs}), mmio_spec);
193198
// Generate the kernel.
194199
kernel_comp = kernel(opts->kernel_name, recordbatch_comps, mmio_comp);
195200
// Generate the nucleus.
196-
nucleus_comp = nucleus(opts->kernel_name + "_Nucleus", recordbatch_comps, kernel_comp, mmio_comp);
201+
nucleus_comp = nucleus(opts->kernel_name + "_Nucleus", recordbatch_comps, kernel_comp, mmio_comp, mmio_spec);
197202
// Generate the mantle.
198-
mantle_comp = mantle(opts->kernel_name + "_Mantle", recordbatch_comps, nucleus_comp, bus_spec);
203+
mantle_comp = mantle(opts->kernel_name + "_Mantle", recordbatch_comps, nucleus_comp, bus_spec, mmio_spec);
199204
}
200205

201-
void Design::RunVhdmmio(const std::vector<std::vector<MmioReg>*>& regs) {
206+
void Design::RunVhdmmio(const std::vector<std::vector<MmioReg> *> &regs, Axi4LiteSpec axi_spec) {
202207
// Generate a Yaml file for vhdmmio based on the recordbatch description
203208
auto ofs = std::ofstream("fletchgen.mmio.yaml");
204-
ofs << GenerateVhdmmioYaml(regs);
209+
ofs << GenerateVhdmmioYaml(regs, axi_spec);
205210
ofs.close();
206211

207212
// Run vhdmmio
213+
// TODO(johanpel): don't do this:
208214
auto vhdmmio_result = system("python3 -m vhdmmio -V vhdl -H -P vhdl > vhdmmio.log");
209215
if (vhdmmio_result != 0) {
210216
FLETCHER_LOG(FATAL, "vhdmmio exited with status " << vhdmmio_result);

codegen/cpp/fletchgen/src/fletchgen/design.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ struct Design {
5959
/// Pointers to all registers vectors.
6060
std::vector<std::vector<MmioReg> *> all_regs = {&default_regs, &recordbatch_regs, &kernel_regs, &profiling_regs};
6161

62+
Axi4LiteSpec mmio_spec;
63+
6264
/// The RecordBatchDescriptions to use in SREC generation.
6365
std::vector<fletcher::RecordBatchDescription> batch_desc;
6466

@@ -87,7 +89,7 @@ struct Design {
8789
static std::vector<MmioReg> ParseCustomRegs(const std::vector<std::string> &regs);
8890

8991
/// @brief Generate vhdmmio yaml and run it.
90-
static void RunVhdmmio(const std::vector<std::vector<MmioReg> *>& regs);
92+
static void RunVhdmmio(const std::vector<std::vector<MmioReg> *> &regs, Axi4LiteSpec axi_spec);
9193
};
9294

9395
} // namespace fletchgen

codegen/cpp/fletchgen/src/fletchgen/fletchgen.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ int fletchgen(int argc, char **argv) {
7171
// Generate the whole Cerata design.
7272
fletchgen::Design design(options);
7373
// Run vhdmmio to generate the mmio infrastructure.
74-
std::thread vhdmmio(Design::RunVhdmmio, design.all_regs);
74+
std::thread vhdmmio(Design::RunVhdmmio, design.all_regs, design.mmio_spec);
7575

7676

7777
// Generate SREC output
@@ -139,7 +139,7 @@ int fletchgen(int argc, char **argv) {
139139
std::string axi_file_path = options->output_dir + "/vhdl/AxiTop.gen.vhd";
140140
FLETCHER_LOG(INFO, "Saving AXI top-level design to: " + axi_file_path);
141141
axi_file = std::ofstream(axi_file_path);
142-
fletchgen::top::GenerateAXITop(*design.mantle_comp, *design.schema_set, {&axi_file});
142+
fletchgen::top::GenerateAXITop(*design.mantle_comp, *design.schema_set, {&axi_file}, design.mmio_spec);
143143
axi_file.close();
144144
}
145145

codegen/cpp/fletchgen/src/fletchgen/mantle.cc

+6-4
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ using cerata::intl;
3939
Mantle::Mantle(std::string name,
4040
const std::vector<std::shared_ptr<RecordBatch>> &recordbatches,
4141
const std::shared_ptr<Nucleus> &nucleus,
42-
BusDim bus_dim)
42+
BusDim bus_dim,
43+
Axi4LiteSpec axi_spec)
4344
: Component(std::move(name)), bus_dim_(bus_dim) {
4445

4546
using std::pair;
@@ -59,7 +60,7 @@ Mantle::Mantle(std::string name,
5960
// Add default ports; bus clock/reset, kernel clock/reset and AXI4-lite port.
6061
auto bcr = port("bcd", cr(), Port::Dir::IN, bus_cd());
6162
auto kcr = port("kcd", cr(), Port::Dir::IN, kernel_cd());
62-
auto axi = axi4_lite(Port::Dir::IN, bus_cd());
63+
auto axi = axi4_lite(Port::Dir::IN, bus_cd(), axi_spec);
6364
Add({bcr, kcr, axi});
6465

6566
// Handle the Nucleus.
@@ -179,8 +180,9 @@ Mantle::Mantle(std::string name,
179180
std::shared_ptr<Mantle> mantle(const std::string &name,
180181
const std::vector<std::shared_ptr<RecordBatch>> &recordbatches,
181182
const std::shared_ptr<Nucleus> &nucleus,
182-
BusDim bus_spec) {
183-
return std::make_shared<Mantle>(name, recordbatches, nucleus, bus_spec);
183+
BusDim bus_spec,
184+
Axi4LiteSpec axi_spec) {
185+
return std::make_shared<Mantle>(name, recordbatches, nucleus, bus_spec, axi_spec);
184186
}
185187

186188
} // namespace fletchgen

codegen/cpp/fletchgen/src/fletchgen/mantle.h

+5-2
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ class Mantle : public Component {
3939
explicit Mantle(std::string name,
4040
const std::vector<std::shared_ptr<RecordBatch>> &recordbatches,
4141
const std::shared_ptr<Nucleus> &nucleus,
42-
BusDim bus_dim);
42+
BusDim bus_dim,
43+
Axi4LiteSpec axi_spec);
4344
/// @brief Return the kernel component of this Mantle.
4445
std::shared_ptr<Nucleus> nucleus() const { return nucleus_; }
4546
/// @brief Return all RecordBatch(Reader/Writer) instances of this Mantle.
@@ -68,11 +69,13 @@ class Mantle : public Component {
6869
* @param recordbatches The RecordBatch components to instantiate.
6970
* @param nucleus The Nucleus to instantiate.
7071
* @param bus_spec The specification of the top-level bus.
72+
* @param axi_spec The specification of the AXI4-lite MMIO interface.
7173
* @return A shared pointer to the mantle component.
7274
*/
7375
std::shared_ptr<Mantle> mantle(const std::string &name,
7476
const std::vector<std::shared_ptr<RecordBatch>> &recordbatches,
7577
const std::shared_ptr<Nucleus> &nucleus,
76-
BusDim bus_spec);
78+
BusDim bus_spec,
79+
Axi4LiteSpec axi_spec);
7780

7881
} // namespace fletchgen

codegen/cpp/fletchgen/src/fletchgen/mmio.cc

+25-16
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ std::shared_ptr<MmioPort> mmio_port(Port::Dir dir, const MmioReg &reg, const std
5959
}
6060

6161
std::shared_ptr<Component> mmio(const std::vector<fletcher::RecordBatchDescription> &batches,
62-
const std::vector<MmioReg> &regs) {
62+
const std::vector<MmioReg> &regs,
63+
Axi4LiteSpec axi_spec) {
6364
// Clock/reset port.
6465
// TODO(johanpel): Everything is on the kernel clock domain now until vhdmmio gets CDC support.
6566
auto kcd = port("kcd", cr(), Port::Dir::IN, kernel_cd());
@@ -74,7 +75,7 @@ std::shared_ptr<Component> mmio(const std::vector<fletcher::RecordBatchDescripti
7475
comp->Add(port);
7576
}
7677
// Add the bus interface.
77-
auto bus = axi4_lite(Port::Dir::IN, bus_cd());
78+
auto bus = axi4_lite(Port::Dir::IN, bus_cd(), axi_spec);
7879
comp->Add(bus);
7980

8081
// This will be a primitive component generated by vhdmmio.
@@ -85,14 +86,21 @@ std::shared_ptr<Component> mmio(const std::vector<fletcher::RecordBatchDescripti
8586
return comp;
8687
}
8788

88-
static size_t AddrSpaceUsed(uint32_t width) {
89-
return 4 * (width / 32 + (width % 32 != 0));
89+
// Calculate how much address space is used by this register, given a width and an alignment.
90+
static size_t AddrSpaceUsed(uint32_t width, uint32_t alignment) {
91+
return (alignment / 8) * (width / alignment + (width % alignment != 0));
9092
}
9193

92-
std::string GenerateVhdmmioYaml(const std::vector<std::vector<MmioReg> *>& regs, std::optional<size_t *> next_addr) {
94+
static size_t Offset(uint32_t address, uint32_t alignment) {
95+
return 8 * (address % (alignment / 8));
96+
}
97+
98+
std::string GenerateVhdmmioYaml(const std::vector<std::vector<MmioReg> *> &regs,
99+
Axi4LiteSpec axi_spec,
100+
std::optional<size_t *> next_addr) {
93101
std::stringstream ss;
94102
// The next free byte address.
95-
size_t next_free_addr = 0;
103+
size_t next_free_addr = axi_spec.offset;
96104
// Header:
97105
ss << "metadata:\n"
98106
" name: mmio\n"
@@ -105,40 +113,41 @@ std::string GenerateVhdmmioYaml(const std::vector<std::vector<MmioReg> *>& regs,
105113
" reset-name: kcd_reset\n"
106114
"\n"
107115
"features:\n"
108-
" bus-width: 32\n"
109-
" optimize: yes\n"
116+
" bus-width: " << std::to_string(axi_spec.data_width) << "\n";
117+
ss << " optimize: yes\n"
110118
"\n"
111119
"interface:\n"
112120
" flatten: yes\n"
113121
"\n"
114122
"fields: \n";
115123

116124
// Iterate over the registers and generate the appropriate YAML lines.
117-
for (auto &sub : regs) {
125+
for (const auto &sub : regs) {
118126
for (auto &r : *sub) {
119127
// Determine the address.
120128
if (r.addr) {
121129
// There is a fixed address.
122-
ss << " - address: " << *r.addr << "\n";
130+
ss << " - address: " << axi_spec.offset + *r.addr << "\n";
123131
// Just take this address plus its space as the next address. This limits how the vector of MmioRegs can be
124132
// supplied (fixed addr. must be at the start of the vector and ordered), but we don't currently use this in
125133
// any other way.
126-
next_free_addr = *r.addr + AddrSpaceUsed(r.width);
134+
next_free_addr = axi_spec.offset + *r.addr + AddrSpaceUsed(r.width, 32);
127135
} else {
128136
// There is not a fixed address.
129137
ss << " - address: " << next_free_addr << "\n";
130138
r.addr = next_free_addr;
131-
next_free_addr += AddrSpaceUsed(r.width);
139+
next_free_addr += AddrSpaceUsed(r.width, 32);
132140
}
133141
// Set doc, name and other stuff.
134142
ss << " name: " << r.name << "\n";
135143
if (!r.desc.empty()) {
136144
ss << " doc: " << r.desc << "\n";
137145
}
146+
auto offset = Offset(r.addr.value(), axi_spec.data_width);
138147
if (r.width > 1) {
139-
ss << " bitrange: " << r.index + r.width - 1 << ".." << r.index << "\n";
148+
ss << " bitrange: " << offset + r.index + r.width - 1 << ".." << offset + r.index << "\n";
140149
} else {
141-
ss << " bitrange: " << r.index << "\n";
150+
ss << " bitrange: " << offset + r.index << "\n";
142151
}
143152
ss << " behavior: " << ToString(r.behavior) << "\n";
144153
ss << "\n";
@@ -154,8 +163,8 @@ std::string GenerateVhdmmioYaml(const std::vector<std::vector<MmioReg> *>& regs,
154163

155164
bool ExposeToKernel(MmioFunction fun) {
156165
switch (fun) {
157-
case MmioFunction::DEFAULT: return true;
158-
case MmioFunction::KERNEL: return true;
166+
case MmioFunction::DEFAULT:
167+
case MmioFunction::KERNEL:
159168
case MmioFunction::BATCH: return true;
160169
default:return false;
161170
}

codegen/cpp/fletchgen/src/fletchgen/mmio.h

+9-3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#include <vector>
2323
#include <unordered_map>
2424

25+
#include "fletchgen/axi4_lite.h"
26+
2527
namespace fletchgen {
2628

2729
using cerata::Component;
@@ -62,7 +64,7 @@ enum class MmioBehavior {
6264
STROBE, ///< Register contents is asserted for one cycle by host software.
6365
};
6466

65-
/// @brief Structure to represent an mmio register
67+
/// @brief Structure to represent an MMIO register
6668
struct MmioReg {
6769
/// @brief MmioReg default constructor.
6870
MmioReg() = default;
@@ -122,9 +124,11 @@ std::shared_ptr<MmioPort> mmio_port(Port::Dir dir, const MmioReg &reg,
122124
* Any fixed addresses in the MmioReg.address field can only occur at the start of the vector set and must be ordered.
123125
*
124126
* @param regs A vector of pointers to vectors of registers. Will be modified in case address was not set.
127+
* @param axi_spec Specification of the AXI4 lite mmio bus.
125128
* @param next_addr Optionally outputs the byte address offset of the next free register address.
126129
*/
127130
std::string GenerateVhdmmioYaml(const std::vector<std::vector<MmioReg> *> &regs,
131+
Axi4LiteSpec axi_spec,
128132
std::optional<size_t *> next_addr = std::nullopt);
129133

130134
/**
@@ -134,10 +138,12 @@ std::string GenerateVhdmmioYaml(const std::vector<std::vector<MmioReg> *> &regs,
134138
* an identical component interface.
135139
*
136140
* @param[in] batches The RecordBatchDescriptions of the recordbatches in the design.
137-
* @param[in] regs A list of custom 32-bit register names.
141+
* @param[in] regs A list of custom 32-bit register names.
142+
* @param[in] axi_spec Specification of the AXI4-lite interface
138143
* @return A component.
139144
*/
140145
std::shared_ptr<Component> mmio(const std::vector<fletcher::RecordBatchDescription> &batches,
141-
const std::vector<MmioReg> &regs);
146+
const std::vector<MmioReg> &regs,
147+
Axi4LiteSpec axi_spec);
142148

143149
} // namespace fletchgen

codegen/cpp/fletchgen/src/fletchgen/nucleus.cc

+6-4
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ static void CopyFieldPorts(Component *nucleus, const RecordBatch &record_batch,
7171
Nucleus::Nucleus(const std::string &name,
7272
const std::vector<std::shared_ptr<RecordBatch>> &recordbatches,
7373
const std::shared_ptr<Kernel> &kernel,
74-
const std::shared_ptr<Component> &mmio)
74+
const std::shared_ptr<Component> &mmio,
75+
Axi4LiteSpec axi_spec)
7576
: Component(name) {
7677
cerata::NodeMap rebinding;
7778

@@ -83,7 +84,7 @@ Nucleus::Nucleus(const std::string &name,
8384
auto kcd = port("kcd", cr(), Port::Dir::IN, kernel_cd());
8485
Add(kcd);
8586
// Add AXI4-lite interface
86-
auto axi = axi4_lite(Port::Dir::IN, bus_cd());
87+
auto axi = axi4_lite(Port::Dir::IN, bus_cd(), axi_spec);
8788
Add(axi);
8889

8990
// Instantiate the kernel and connect the clock/reset.
@@ -212,8 +213,9 @@ Nucleus::Nucleus(const std::string &name,
212213
std::shared_ptr<Nucleus> nucleus(const std::string &name,
213214
const std::vector<std::shared_ptr<RecordBatch>> &recordbatches,
214215
const std::shared_ptr<Kernel> &kernel,
215-
const std::shared_ptr<Component> &mmio) {
216-
return std::make_shared<Nucleus>(name, recordbatches, kernel, mmio);
216+
const std::shared_ptr<Component> &mmio,
217+
Axi4LiteSpec axi_spec) {
218+
return std::make_shared<Nucleus>(name, recordbatches, kernel, mmio, axi_spec);
217219
}
218220

219221
std::vector<FieldPort *> Nucleus::GetFieldPorts(FieldPort::Function fun) const {

codegen/cpp/fletchgen/src/fletchgen/nucleus.h

+5-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "fletchgen/kernel.h"
2828
#include "fletchgen/array.h"
2929
#include "fletchgen/mmio.h"
30+
#include "fletchgen/axi4_lite.h"
3031

3132
namespace fletchgen {
3233

@@ -42,7 +43,8 @@ struct Nucleus : Component {
4243
explicit Nucleus(const std::string &name,
4344
const std::vector<std::shared_ptr<RecordBatch>> &recordbatches,
4445
const std::shared_ptr<Kernel> &kernel,
45-
const std::shared_ptr<Component> &mmio);
46+
const std::shared_ptr<Component> &mmio,
47+
Axi4LiteSpec axi_spec);
4648

4749
/// @brief Return all field-derived ports with a specific function.
4850
std::vector<FieldPort *> GetFieldPorts(FieldPort::Function fun) const;
@@ -60,6 +62,7 @@ struct Nucleus : Component {
6062
std::shared_ptr<Nucleus> nucleus(const std::string &name,
6163
const std::vector<std::shared_ptr<RecordBatch>> &recordbatches,
6264
const std::shared_ptr<Kernel> &kernel,
63-
const std::shared_ptr<Component> &mmio);
65+
const std::shared_ptr<Component> &mmio,
66+
Axi4LiteSpec axi_spec);
6467

6568
} // namespace fletchgen

codegen/cpp/fletchgen/src/fletchgen/options.cc

+7-3
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,12 @@ bool Options::Parse(Options *options, int argc, char **argv) {
7979
" bm : Bus maximum burst size.\n"
8080
"Currently supports only one top-level bus specification. Default: \"64,512,64,8,1,16\"");
8181

82-
app.add_flag("--axi", options->axi_top,
83-
"Generate AXI top-level template (VHDL only).");
82+
app.add_flag("--mmio64", options->mmio64, "Use a 64-bits AXI4-lite MMIO data bus instead of 32-bits.");
83+
app.add_option("--mmio-offset", options->mmio_offset, "AXI4 offset address for Fletcher registers.");
84+
//app.add_option("--axi4l-addr-width", options->axi4_lite_aw, "TODO: Width of the AXI4-lite address bus (Default:32).");
85+
86+
app.add_flag("--axi", options->axi_top, "Generate AXI top-level template (VHDL only).");
87+
8488
app.add_flag("--sim", options->sim_top,
8589
"Generate simulation top-level template (VHDL only).");
8690
app.add_flag("--vivado_hls", options->vivado_hls,
@@ -138,7 +142,7 @@ static bool HasLanguage(const std::vector<std::string> &languages, const std::st
138142
return false;
139143
}
140144

141-
bool Options::MustGenerate(const std::string& lang) const {
145+
bool Options::MustGenerate(const std::string &lang) const {
142146
return HasLanguage(languages, lang) && Options::MustGenerateDesign();
143147
}
144148

0 commit comments

Comments
 (0)