diff --git a/himbaechel/uarch/gatemate/config.cc b/himbaechel/uarch/gatemate/config.cc new file mode 100644 index 00000000..980bc521 --- /dev/null +++ b/himbaechel/uarch/gatemate/config.cc @@ -0,0 +1,199 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Peppercorn Authors. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "config.h" +#include +#include +#include +#include "log.h" + +NEXTPNR_NAMESPACE_BEGIN + +#define fmt(x) (static_cast(std::ostringstream() << x).str()) + +inline std::string to_string(const std::vector &bv) +{ + std::ostringstream os; + for (auto bit : boost::adaptors::reverse(bv)) + os << (bit ? '1' : '0'); + return os.str(); +} + +inline std::istream &operator>>(std::istream &in, std::vector &bv) +{ + bv.clear(); + std::string s; + in >> s; + for (auto c : boost::adaptors::reverse(s)) { + assert((c == '0') || (c == '1')); + bv.push_back((c == '1')); + } + return in; +} + +// Skip whitespace, optionally including newlines +inline void skip_blank(std::istream &in, bool nl = false) +{ + int c = in.peek(); + while (in && (((c == ' ') || (c == '\t')) || (nl && ((c == '\n') || (c == '\r'))))) { + in.get(); + c = in.peek(); + } +} +// Return true if end of line (or file) +inline bool skip_check_eol(std::istream &in) +{ + skip_blank(in, false); + if (!in) + return false; + int c = in.peek(); + // Comments count as end of line + if (c == '#') { + in.get(); + c = in.peek(); + while (in && c != EOF && c != '\n') { + in.get(); + c = in.peek(); + } + return true; + } + return (c == EOF || c == '\n'); +} + +// Skip past blank lines and comments +inline void skip(std::istream &in) +{ + skip_blank(in, true); + while (in && (in.peek() == '#')) { + // Skip comment line + skip_check_eol(in); + skip_blank(in, true); + } +} + +// Return true if at the end of a record (or file) +inline bool skip_check_eor(std::istream &in) +{ + skip(in); + int c = in.peek(); + return (c == EOF || c == '.'); +} + +// Return true if at the end of file +inline bool skip_check_eof(std::istream &in) +{ + skip(in); + int c = in.peek(); + return (c == EOF); +} + +std::ostream &operator<<(std::ostream &out, const ConfigWord &cw) +{ + out << cw.name << " " << to_string(cw.value) << std::endl; + return out; +} + +std::istream &operator>>(std::istream &in, ConfigWord &cw) +{ + in >> cw.name; + in >> cw.value; + return in; +} + +std::ostream &operator<<(std::ostream &out, const TileConfig &tc) +{ + for (const auto &cword : tc.cwords) + out << cword; + return out; +} + +std::istream &operator>>(std::istream &in, TileConfig &tc) +{ + tc.cwords.clear(); + while (!skip_check_eor(in)) { + ConfigWord w; + in >> w; + tc.cwords.push_back(w); + } + return in; +} + +void TileConfig::add_word(const std::string &name, const std::vector &value) { cwords.push_back({name, value}); } + +std::string TileConfig::to_string() const +{ + std::stringstream ss; + ss << *this; + return ss.str(); +} + +TileConfig TileConfig::from_string(const std::string &str) +{ + std::stringstream ss(str); + TileConfig tc; + ss >> tc; + return tc; +} + +bool TileConfig::empty() const { return cwords.empty(); } + +std::ostream &operator<<(std::ostream &out, const ChipConfig &cc) +{ + out << ".device " << cc.chip_name << std::endl << std::endl; + for (const auto &config : cc.configs) { + if (!config.second.empty()) { + out << ".config " << config.first << " " << std::endl; + out << config.second; + out << std::endl; + } + } + for (const auto &tile : cc.tiles) { + if (!tile.second.empty()) { + out << ".tile " << tile.first.die << " " << tile.first.x << " " << tile.first.y << std::endl; + out << tile.second; + out << std::endl; + } + } + for (const auto &bram : cc.brams) { + if (!bram.second.empty()) { + out << ".bram " << bram.first.die << " " << bram.first.x << " " << bram.first.y << std::endl; + out << bram.second; + out << std::endl; + } + } + for (const auto &bram : cc.bram_data) { + if (!bram.second.empty()) { + out << ".bram_init " << bram.first.die << " " << bram.first.x << " " << bram.first.y << std::endl; + std::ios_base::fmtflags f(out.flags()); + for (size_t i = 0; i < bram.second.size(); i++) { + out << std::setw(2) << std::setfill('0') << std::hex << (int)bram.second.at(i); + if (i % 32 == 31) + out << std::endl; + else + out << " "; + } + out.flags(f); + out << std::endl; + } + } + + return out; +} + +NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/gatemate/config.h b/himbaechel/uarch/gatemate/config.h new file mode 100644 index 00000000..89e5b6e4 --- /dev/null +++ b/himbaechel/uarch/gatemate/config.h @@ -0,0 +1,88 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Peppercorn Authors. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef GATEMATE_CONFIG_H +#define GATEMATE_CONFIG_H + +#include +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +struct ConfigWord +{ + std::string name; + std::vector value; + inline bool operator==(const ConfigWord &other) const { return other.name == name && other.value == value; } +}; + +std::ostream &operator<<(std::ostream &out, const ConfigWord &cw); + +std::istream &operator>>(std::istream &in, ConfigWord &cw); + +struct TileConfig +{ + std::vector cwords; + + void add_word(const std::string &name, const std::vector &value); + + std::string to_string() const; + static TileConfig from_string(const std::string &str); + + bool empty() const; +}; + +std::ostream &operator<<(std::ostream &out, const TileConfig &tc); + +std::istream &operator>>(std::istream &in, TileConfig &ce); + +struct CfgLoc +{ + int die; + int x; + int y; + + inline bool operator==(const CfgLoc &other) const { return other.die == die && other.x == x && other.y == y; } + inline bool operator!=(const CfgLoc &other) const { return other.die != die || x != other.x || y == other.y; } + + inline bool operator<(const CfgLoc &other) const + { + return die < other.die || + ((die == other.die && y < other.y) || (die == other.die && y == other.y && x < other.x)); + } +}; + +class ChipConfig +{ + public: + std::string chip_name; + std::string chip_package; + std::map tiles; + std::map brams; + std::map configs; + + // Block RAM initialisation + std::map> bram_data; +}; + +std::ostream &operator<<(std::ostream &out, const ChipConfig &cc); + +NEXTPNR_NAMESPACE_END + +#endif diff --git a/himbaechel/uarch/gatemate/constids.inc b/himbaechel/uarch/gatemate/constids.inc index ec5d5a30..8c1ecc92 100644 --- a/himbaechel/uarch/gatemate/constids.inc +++ b/himbaechel/uarch/gatemate/constids.inc @@ -34,3 +34,15 @@ X(POUTY2) X(GPIO) +X(CC_IBUF) +X(CC_OBUF) +X(I) +X(O) +X(DI) +X(DO) +X(OE) +X(Y) +X(A) + +X(OUT3) +X(OUT4) diff --git a/himbaechel/uarch/gatemate/extra_data.h b/himbaechel/uarch/gatemate/extra_data.h index 1df98075..3d0ad9bd 100644 --- a/himbaechel/uarch/gatemate/extra_data.h +++ b/himbaechel/uarch/gatemate/extra_data.h @@ -26,8 +26,10 @@ NEXTPNR_NAMESPACE_BEGIN NPNR_PACKED_STRUCT(struct GateMatePipExtraDataPOD { int32_t name; - uint16_t bits; - uint16_t value; + uint8_t bits; + uint8_t value; + uint8_t invert; + uint8_t dummy; }); NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/gatemate/gatemate.cc b/himbaechel/uarch/gatemate/gatemate.cc index a48f9eef..028d8f13 100644 --- a/himbaechel/uarch/gatemate/gatemate.cc +++ b/himbaechel/uarch/gatemate/gatemate.cc @@ -17,6 +17,7 @@ * */ +#include #include "extra_data.h" #include "himbaechel_api.h" #include "log.h" @@ -26,6 +27,7 @@ #include "himbaechel_helpers.h" #include "gatemate.h" +#include "config.h" #define GEN_INIT_CONSTIDS #define HIMBAECHEL_CONSTIDS "uarch/gatemate/constids.inc" @@ -76,6 +78,140 @@ void GateMateImpl::drawBel(std::vector &g, GraphicElement::style } } +void GateMateImpl::pack() +{ + const pool top_ports{ + CellTypePort(id_CC_IBUF, id_I), + CellTypePort(id_CC_OBUF, id_O), + }; + h.remove_nextpnr_iobs(top_ports); + + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_CC_IBUF, id_CC_OBUF)) + continue; + if (ci.type == id_CC_IBUF) { + ci.renamePort(id_I, id_DI); + ci.renamePort(id_Y, id_IN1); + ci.params[ctx->id("INIT")] = Property("000000000000000000000001000000000000000000000000000000000000000001010000"); + // x=-2 y=99 + BelId bel = ctx->getBelByName(IdStringList::concat(ctx->idf("X%dY%d",-2+2,99+2), id_GPIO)); + ctx->bindBel(bel, &ci, PlaceStrength::STRENGTH_FIXED); + } + if (ci.type == id_CC_OBUF) { + ci.renamePort(id_O, id_DO); + ci.renamePort(id_A, id_OUT2); + ci.params[ctx->id("INIT")] = Property("000000000000000000000000000000000000000100000000000000010000100100000000"); + // x=-2 y=95 + BelId bel = ctx->getBelByName(IdStringList::concat(ctx->idf("X%dY%d",-2+2,95+2), id_GPIO)); + ctx->bindBel(bel, &ci, PlaceStrength::STRENGTH_FIXED); + } + ci.type = id_GPIO; + } +} + +delay_t GateMateImpl::estimateDelay(WireId src, WireId dst) const +{ + int sx, sy, dx, dy; + tile_xy(ctx->chip_info, src.tile, sx, sy); + tile_xy(ctx->chip_info, dst.tile, dx, dy); + + return 100 * (std::abs(dx - sx)/4 + std::abs(dy - sy)/4 + 2); +} + +void get_bitstream_tile(int x,int y,int &b_x,int &b_y) +{ + // Edge blocks are bit bigger + if (x==-2) x++; + if (x==163) x--; + if (y==-2) y++; + if (y==131) y--; + b_x = (x + 1) / 2; + b_y = (y + 1) / 2; +} + +std::vector int_to_bitvector(int val, int size) +{ + std::vector bv; + for (int i = 0; i < size; i++) { + bv.push_back((val & (1 << i)) != 0); + } + return bv; +} + +std::vector str_to_bitvector(std::string str, int size) +{ + std::vector bv; + bv.resize(size, 0); + for (int i = 0; i < int(str.size()); i++) { + char c = str.at((str.size() - i) - 1); + NPNR_ASSERT(c == '0' || c == '1'); + bv.at(i) = (c == '1'); + } + return bv; +} + +CfgLoc getConfigLoc(Context *ctx, int tile) +{ + int x0, y0; + int bx, by; + tile_xy(ctx->chip_info, tile, x0, y0); + get_bitstream_tile(x0 - 2,y0 - 2, bx, by); + CfgLoc loc; + loc.die = 0; + loc.x = bx; + loc.y = by; + return loc; +} + +void GateMateImpl::postRoute() +{ + const ArchArgs &args = ctx->args; + if (args.options.count("out")) { + ChipConfig cc; + cc.chip_name = args.device; + cc.configs[0].add_word("GPIO.BANK_E1", int_to_bitvector(1,1)); + cc.configs[0].add_word("GPIO.BANK_E2", int_to_bitvector(1,1)); + cc.configs[0].add_word("GPIO.BANK_N1", int_to_bitvector(1,1)); + cc.configs[0].add_word("GPIO.BANK_N2", int_to_bitvector(1,1)); + cc.configs[0].add_word("GPIO.BANK_S1", int_to_bitvector(1,1)); + cc.configs[0].add_word("GPIO.BANK_S2", int_to_bitvector(1,1)); + cc.configs[0].add_word("GPIO.BANK_W1", int_to_bitvector(1,1)); + cc.configs[0].add_word("GPIO.BANK_W2", int_to_bitvector(1,1)); + for (auto &cell : ctx->cells) { + switch (cell.second->type.index) { + case id_GPIO.index: { + CfgLoc loc = getConfigLoc(ctx, cell.second.get()->bel.tile); + cc.tiles[loc].add_word("GPIO.INIT", str_to_bitvector(str_or_default(cell.second.get()->params,ctx->id("INIT"),""), 72)); + break; + } + default: + break; + } + } + + for (auto &net : ctx->nets) { + NetInfo *ni = net.second.get(); + if (ni->wires.empty()) + continue; + std::set nets; + for (auto &w : ni->wires) { + if (w.second.pip != PipId()) { + PipId pip = w.second.pip; + const auto extra_data = *reinterpret_cast(chip_pip_info(ctx->chip_info, pip).extra_data.get()); + if (extra_data.name!=0) { + IdString name = IdString(extra_data.name); + CfgLoc loc = getConfigLoc(ctx, pip.tile); + cc.tiles[loc].add_word(name.c_str(ctx),int_to_bitvector(extra_data.value, extra_data.bits)); + } + } + } + } + std::ofstream out_config(args.options.at("out")); + out_config << cc; + } +} + struct GateMateArch : HimbaechelArch { GateMateArch() : HimbaechelArch("gatemate") {}; diff --git a/himbaechel/uarch/gatemate/gatemate.h b/himbaechel/uarch/gatemate/gatemate.h index b00c907b..7e679977 100644 --- a/himbaechel/uarch/gatemate/gatemate.h +++ b/himbaechel/uarch/gatemate/gatemate.h @@ -37,6 +37,12 @@ struct GateMateImpl : HimbaechelAPI void init(Context *ctx) override; + void pack() override; + + void postRoute() override; + + delay_t estimateDelay(WireId src, WireId dst) const override; + void drawBel(std::vector &g, GraphicElement::style_t style, IdString bel_type, Loc loc) override; private: diff --git a/himbaechel/uarch/gatemate/gen/arch_gen.py b/himbaechel/uarch/gatemate/gen/arch_gen.py index 3f5ef66c..f44b7b00 100644 --- a/himbaechel/uarch/gatemate/gen/arch_gen.py +++ b/himbaechel/uarch/gatemate/gen/arch_gen.py @@ -40,13 +40,16 @@ class PipExtraData(BBAStruct): name: IdString bits: int = 0 value: int = 0 + invert: int = 0 def serialise_lists(self, context: str, bba: BBAWriter): pass def serialise(self, context: str, bba: BBAWriter): bba.u32(self.name.index) - bba.u16(self.bits) - bba.u16(self.value) + bba.u8(self.bits) + bba.u8(self.value) + bba.u8(self.invert) + bba.u8(0) # dummy def set_timings(ch): speed = "DEFAULT" @@ -72,7 +75,12 @@ def main(): tt.add_bel_pin(bel, pin.name, f"{prim.name}.{pin.name}", pin.dir) for mux in die.get_mux_connections_for_type(type_name): pp = tt.create_pip(mux.src, mux.dst) - pp.extra_data = PipExtraData(ch.strs.id(mux.name), mux.bits, mux.value) + pp.extra_data = PipExtraData(ch.strs.id(mux.name), mux.bits, mux.value, mux.invert) + if "CPE" in type_name: + tt.create_pip("CPE.IN1", "CPE.OUT1") + tt.create_pip("CPE.IN1", "CPE.OUT2") + tt.create_pip("CPE.IN1", "CPE.RAM_O1") + tt.create_pip("CPE.IN1", "CPE.RAM_O2") # Setup tile grid for x in range(die.max_col() + 3):