Skip to content

Commit

Permalink
Merge pull request #241 from abs-tudelft/mmio-widths
Browse files Browse the repository at this point in the history
Add 64-bit data bus option for AXI4-lite MMIO
  • Loading branch information
johanpel authored Sep 28, 2020
2 parents 1d9cd0c + 0466afa commit fe4b912
Show file tree
Hide file tree
Showing 22 changed files with 172 additions and 118 deletions.
16 changes: 12 additions & 4 deletions codegen/cpp/fletchgen/src/fletchgen/axi4_lite.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,27 @@ using cerata::ClockDomain;
using cerata::Type;
using cerata::Port;

/// @brief AXI-lite bus width specification. Address is always 32, but data can also be 64. Modifiable.
/// @brief AXI4-lite bus specification.
struct Axi4LiteSpec {
/// The MMIO bus data width.
explicit Axi4LiteSpec(size_t data_width = 32, size_t addr_width = 32, size_t offset = 0)
: data_width(data_width), addr_width(addr_width), offset(offset) {
if (offset % (data_width / 8) != 0) {
throw std::runtime_error("Offset must be integer multiple of data width / 8.");
}
}
/// The data width.
size_t data_width = 32;
/// The MMIO bus address width.
/// The address width.
size_t addr_width = 32;
/// The offset for all registers.
size_t offset = 0;
/// @brief Return a human-readable representation of this Axi4LiteSpec.
[[nodiscard]] std::string ToString() const;
/// @brief Return a Cerata type name based on this Axi4LiteSpec.
[[nodiscard]] std::string ToAxiTypeName() const;
};

/// An AXI4-lite port derived from an AXI-lite width specification.
/// An AXI4-lite port derived from an AXI4-lite specification.
struct Axi4LitePort : public Port {
/// The specification this port was derived from.
Axi4LiteSpec spec_;
Expand Down
16 changes: 11 additions & 5 deletions codegen/cpp/fletchgen/src/fletchgen/design.cc
Original file line number Diff line number Diff line change
Expand Up @@ -186,25 +186,31 @@ Design::Design(const std::shared_ptr<Options> &opts) {
kernel_regs = ParseCustomRegs(opts->regs);
profiling_regs = GetProfilingRegs(recordbatch_comps);

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

// Determine width of the AXI4-lite MMIO.
mmio_spec = Axi4LiteSpec(opts->mmio64 ? 64 : 32, opts->mmio_addr_width, opts->mmio_offset);

// Generate the MMIO component.
mmio_comp = mmio(batch_desc, cerata::Merge({default_regs, recordbatch_regs, kernel_regs, profiling_regs}));
mmio_comp =
mmio(batch_desc, cerata::Merge({default_regs, recordbatch_regs, kernel_regs, profiling_regs}), mmio_spec);
// Generate the kernel.
kernel_comp = kernel(opts->kernel_name, recordbatch_comps, mmio_comp);
// Generate the nucleus.
nucleus_comp = nucleus(opts->kernel_name + "_Nucleus", recordbatch_comps, kernel_comp, mmio_comp);
nucleus_comp = nucleus(opts->kernel_name + "_Nucleus", recordbatch_comps, kernel_comp, mmio_comp, mmio_spec);
// Generate the mantle.
mantle_comp = mantle(opts->kernel_name + "_Mantle", recordbatch_comps, nucleus_comp, bus_spec);
mantle_comp = mantle(opts->kernel_name + "_Mantle", recordbatch_comps, nucleus_comp, bus_spec, mmio_spec);
}

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

// Run vhdmmio
// TODO(johanpel): don't do this:
auto vhdmmio_result = system("python3 -m vhdmmio -V vhdl -H -P vhdl > vhdmmio.log");
if (vhdmmio_result != 0) {
FLETCHER_LOG(FATAL, "vhdmmio exited with status " << vhdmmio_result);
Expand Down
4 changes: 3 additions & 1 deletion codegen/cpp/fletchgen/src/fletchgen/design.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ struct Design {
/// Pointers to all registers vectors.
std::vector<std::vector<MmioReg> *> all_regs = {&default_regs, &recordbatch_regs, &kernel_regs, &profiling_regs};

Axi4LiteSpec mmio_spec;

/// The RecordBatchDescriptions to use in SREC generation.
std::vector<fletcher::RecordBatchDescription> batch_desc;

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

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

} // namespace fletchgen
4 changes: 2 additions & 2 deletions codegen/cpp/fletchgen/src/fletchgen/fletchgen.cc
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ int fletchgen(int argc, char **argv) {
// Generate the whole Cerata design.
fletchgen::Design design(options);
// Run vhdmmio to generate the mmio infrastructure.
std::thread vhdmmio(Design::RunVhdmmio, design.all_regs);
std::thread vhdmmio(Design::RunVhdmmio, design.all_regs, design.mmio_spec);


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

Expand Down
10 changes: 6 additions & 4 deletions codegen/cpp/fletchgen/src/fletchgen/mantle.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ using cerata::intl;
Mantle::Mantle(std::string name,
const std::vector<std::shared_ptr<RecordBatch>> &recordbatches,
const std::shared_ptr<Nucleus> &nucleus,
BusDim bus_dim)
BusDim bus_dim,
Axi4LiteSpec axi_spec)
: Component(std::move(name)), bus_dim_(bus_dim) {

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

// Handle the Nucleus.
Expand Down Expand Up @@ -179,8 +180,9 @@ Mantle::Mantle(std::string name,
std::shared_ptr<Mantle> mantle(const std::string &name,
const std::vector<std::shared_ptr<RecordBatch>> &recordbatches,
const std::shared_ptr<Nucleus> &nucleus,
BusDim bus_spec) {
return std::make_shared<Mantle>(name, recordbatches, nucleus, bus_spec);
BusDim bus_spec,
Axi4LiteSpec axi_spec) {
return std::make_shared<Mantle>(name, recordbatches, nucleus, bus_spec, axi_spec);
}

} // namespace fletchgen
7 changes: 5 additions & 2 deletions codegen/cpp/fletchgen/src/fletchgen/mantle.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ class Mantle : public Component {
explicit Mantle(std::string name,
const std::vector<std::shared_ptr<RecordBatch>> &recordbatches,
const std::shared_ptr<Nucleus> &nucleus,
BusDim bus_dim);
BusDim bus_dim,
Axi4LiteSpec axi_spec);
/// @brief Return the kernel component of this Mantle.
std::shared_ptr<Nucleus> nucleus() const { return nucleus_; }
/// @brief Return all RecordBatch(Reader/Writer) instances of this Mantle.
Expand Down Expand Up @@ -68,11 +69,13 @@ class Mantle : public Component {
* @param recordbatches The RecordBatch components to instantiate.
* @param nucleus The Nucleus to instantiate.
* @param bus_spec The specification of the top-level bus.
* @param axi_spec The specification of the AXI4-lite MMIO interface.
* @return A shared pointer to the mantle component.
*/
std::shared_ptr<Mantle> mantle(const std::string &name,
const std::vector<std::shared_ptr<RecordBatch>> &recordbatches,
const std::shared_ptr<Nucleus> &nucleus,
BusDim bus_spec);
BusDim bus_spec,
Axi4LiteSpec axi_spec);

} // namespace fletchgen
41 changes: 25 additions & 16 deletions codegen/cpp/fletchgen/src/fletchgen/mmio.cc
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ std::shared_ptr<MmioPort> mmio_port(Port::Dir dir, const MmioReg &reg, const std
}

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

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

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

std::string GenerateVhdmmioYaml(const std::vector<std::vector<MmioReg> *>& regs, std::optional<size_t *> next_addr) {
static size_t Offset(uint32_t address, uint32_t alignment) {
return 8 * (address % (alignment / 8));
}

std::string GenerateVhdmmioYaml(const std::vector<std::vector<MmioReg> *> &regs,
Axi4LiteSpec axi_spec,
std::optional<size_t *> next_addr) {
std::stringstream ss;
// The next free byte address.
size_t next_free_addr = 0;
size_t next_free_addr = axi_spec.offset;
// Header:
ss << "metadata:\n"
" name: mmio\n"
Expand All @@ -105,40 +113,41 @@ std::string GenerateVhdmmioYaml(const std::vector<std::vector<MmioReg> *>& regs,
" reset-name: kcd_reset\n"
"\n"
"features:\n"
" bus-width: 32\n"
" optimize: yes\n"
" bus-width: " << std::to_string(axi_spec.data_width) << "\n";
ss << " optimize: yes\n"
"\n"
"interface:\n"
" flatten: yes\n"
"\n"
"fields: \n";

// Iterate over the registers and generate the appropriate YAML lines.
for (auto &sub : regs) {
for (const auto &sub : regs) {
for (auto &r : *sub) {
// Determine the address.
if (r.addr) {
// There is a fixed address.
ss << " - address: " << *r.addr << "\n";
ss << " - address: " << axi_spec.offset + *r.addr << "\n";
// Just take this address plus its space as the next address. This limits how the vector of MmioRegs can be
// supplied (fixed addr. must be at the start of the vector and ordered), but we don't currently use this in
// any other way.
next_free_addr = *r.addr + AddrSpaceUsed(r.width);
next_free_addr = axi_spec.offset + *r.addr + AddrSpaceUsed(r.width, 32);
} else {
// There is not a fixed address.
ss << " - address: " << next_free_addr << "\n";
r.addr = next_free_addr;
next_free_addr += AddrSpaceUsed(r.width);
next_free_addr += AddrSpaceUsed(r.width, 32);
}
// Set doc, name and other stuff.
ss << " name: " << r.name << "\n";
if (!r.desc.empty()) {
ss << " doc: " << r.desc << "\n";
}
auto offset = Offset(r.addr.value(), axi_spec.data_width);
if (r.width > 1) {
ss << " bitrange: " << r.index + r.width - 1 << ".." << r.index << "\n";
ss << " bitrange: " << offset + r.index + r.width - 1 << ".." << offset + r.index << "\n";
} else {
ss << " bitrange: " << r.index << "\n";
ss << " bitrange: " << offset + r.index << "\n";
}
ss << " behavior: " << ToString(r.behavior) << "\n";
ss << "\n";
Expand All @@ -154,8 +163,8 @@ std::string GenerateVhdmmioYaml(const std::vector<std::vector<MmioReg> *>& regs,

bool ExposeToKernel(MmioFunction fun) {
switch (fun) {
case MmioFunction::DEFAULT: return true;
case MmioFunction::KERNEL: return true;
case MmioFunction::DEFAULT:
case MmioFunction::KERNEL:
case MmioFunction::BATCH: return true;
default:return false;
}
Expand Down
12 changes: 9 additions & 3 deletions codegen/cpp/fletchgen/src/fletchgen/mmio.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
#include <vector>
#include <unordered_map>

#include "fletchgen/axi4_lite.h"

namespace fletchgen {

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

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

/**
Expand All @@ -134,10 +138,12 @@ std::string GenerateVhdmmioYaml(const std::vector<std::vector<MmioReg> *> &regs,
* an identical component interface.
*
* @param[in] batches The RecordBatchDescriptions of the recordbatches in the design.
* @param[in] regs A list of custom 32-bit register names.
* @param[in] regs A list of custom 32-bit register names.
* @param[in] axi_spec Specification of the AXI4-lite interface
* @return A component.
*/
std::shared_ptr<Component> mmio(const std::vector<fletcher::RecordBatchDescription> &batches,
const std::vector<MmioReg> &regs);
const std::vector<MmioReg> &regs,
Axi4LiteSpec axi_spec);

} // namespace fletchgen
10 changes: 6 additions & 4 deletions codegen/cpp/fletchgen/src/fletchgen/nucleus.cc
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ static void CopyFieldPorts(Component *nucleus, const RecordBatch &record_batch,
Nucleus::Nucleus(const std::string &name,
const std::vector<std::shared_ptr<RecordBatch>> &recordbatches,
const std::shared_ptr<Kernel> &kernel,
const std::shared_ptr<Component> &mmio)
const std::shared_ptr<Component> &mmio,
Axi4LiteSpec axi_spec)
: Component(name) {
cerata::NodeMap rebinding;

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

// Instantiate the kernel and connect the clock/reset.
Expand Down Expand Up @@ -212,8 +213,9 @@ Nucleus::Nucleus(const std::string &name,
std::shared_ptr<Nucleus> nucleus(const std::string &name,
const std::vector<std::shared_ptr<RecordBatch>> &recordbatches,
const std::shared_ptr<Kernel> &kernel,
const std::shared_ptr<Component> &mmio) {
return std::make_shared<Nucleus>(name, recordbatches, kernel, mmio);
const std::shared_ptr<Component> &mmio,
Axi4LiteSpec axi_spec) {
return std::make_shared<Nucleus>(name, recordbatches, kernel, mmio, axi_spec);
}

std::vector<FieldPort *> Nucleus::GetFieldPorts(FieldPort::Function fun) const {
Expand Down
7 changes: 5 additions & 2 deletions codegen/cpp/fletchgen/src/fletchgen/nucleus.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "fletchgen/kernel.h"
#include "fletchgen/array.h"
#include "fletchgen/mmio.h"
#include "fletchgen/axi4_lite.h"

namespace fletchgen {

Expand All @@ -42,7 +43,8 @@ struct Nucleus : Component {
explicit Nucleus(const std::string &name,
const std::vector<std::shared_ptr<RecordBatch>> &recordbatches,
const std::shared_ptr<Kernel> &kernel,
const std::shared_ptr<Component> &mmio);
const std::shared_ptr<Component> &mmio,
Axi4LiteSpec axi_spec);

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

} // namespace fletchgen
10 changes: 7 additions & 3 deletions codegen/cpp/fletchgen/src/fletchgen/options.cc
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,12 @@ bool Options::Parse(Options *options, int argc, char **argv) {
" bm : Bus maximum burst size.\n"
"Currently supports only one top-level bus specification. Default: \"64,512,64,8,1,16\"");

app.add_flag("--axi", options->axi_top,
"Generate AXI top-level template (VHDL only).");
app.add_flag("--mmio64", options->mmio64, "Use a 64-bits AXI4-lite MMIO data bus instead of 32-bits.");
app.add_option("--mmio-offset", options->mmio_offset, "AXI4 offset address for Fletcher registers.");
//app.add_option("--axi4l-addr-width", options->axi4_lite_aw, "TODO: Width of the AXI4-lite address bus (Default:32).");

app.add_flag("--axi", options->axi_top, "Generate AXI top-level template (VHDL only).");

app.add_flag("--sim", options->sim_top,
"Generate simulation top-level template (VHDL only).");
app.add_flag("--vivado_hls", options->vivado_hls,
Expand Down Expand Up @@ -138,7 +142,7 @@ static bool HasLanguage(const std::vector<std::string> &languages, const std::st
return false;
}

bool Options::MustGenerate(const std::string& lang) const {
bool Options::MustGenerate(const std::string &lang) const {
return HasLanguage(languages, lang) && Options::MustGenerateDesign();
}

Expand Down
Loading

0 comments on commit fe4b912

Please sign in to comment.