diff options
-rw-r--r-- | .cirrus.yml | 2 | ||||
-rw-r--r-- | generic/arch.cc | 66 | ||||
-rw-r--r-- | generic/arch.h | 5 | ||||
-rw-r--r-- | generic/examples/.gitignore | 3 | ||||
-rw-r--r-- | generic/examples/blinky.v | 13 | ||||
-rw-r--r-- | generic/examples/blinky_tb.v | 38 | ||||
-rwxr-xr-x | generic/examples/simtest.sh | 7 | ||||
-rw-r--r-- | generic/main.cc | 6 | ||||
-rw-r--r-- | generic/pack.cc | 4 | ||||
-rw-r--r-- | generic/synth/prims.v | 13 | ||||
-rw-r--r-- | json/jsonwrite.cc | 56 |
11 files changed, 155 insertions, 58 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 5cac994e..bdb3c48c 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -11,6 +11,6 @@ task: test_ice40_script: cd build && ./nextpnr-ice40-test smoketest_ice40_script: export NEXTPNR=$(pwd)/build/nextpnr-ice40 && cd ice40/smoketest/attosoc && ./smoketest.sh test_ecp5_script: cd build && ./nextpnr-ecp5-test - smoketest_generic_script: export NEXTPNR=$(pwd)/build/nextpnr-generic && cd generic/examples && ./simple.sh + smoketest_generic_script: export NEXTPNR=$(pwd)/build/nextpnr-generic && cd generic/examples && ./simple.sh && ./simtest.sh regressiontest_ice40_script: make -j $(nproc) -C tests/ice40/regressions NPNR=$(pwd)/build/nextpnr-ice40 regressiontest_ecp5_script: make -j $(nproc) -C tests/ecp5/regressions NPNR=$(pwd)/build/nextpnr-ecp5 diff --git a/generic/arch.cc b/generic/arch.cc index 14d15115..c1c01d26 100644 --- a/generic/arch.cc +++ b/generic/arch.cc @@ -27,6 +27,30 @@ NEXTPNR_NAMESPACE_BEGIN +WireInfo &Arch::wire_info(IdString wire) +{ + auto w = wires.find(wire); + if (w == wires.end()) + NPNR_ASSERT_FALSE_STR("no wire named " + wire.str(this)); + return w->second; +} + +PipInfo &Arch::pip_info(IdString pip) +{ + auto p = pips.find(pip); + if (p == pips.end()) + NPNR_ASSERT_FALSE_STR("no pip named " + pip.str(this)); + return p->second; +} + +BelInfo &Arch::bel_info(IdString bel) +{ + auto b = bels.find(bel); + if (b == bels.end()) + NPNR_ASSERT_FALSE_STR("no bel named " + bel.str(this)); + return b->second; +} + void Arch::addWire(IdString name, IdString type, int x, int y) { NPNR_ASSERT(wires.count(name) == 0); @@ -50,8 +74,8 @@ void Arch::addPip(IdString name, IdString type, IdString srcWire, IdString dstWi pi.delay = delay; pi.loc = loc; - wires.at(srcWire).downhill.push_back(name); - wires.at(dstWire).uphill.push_back(name); + wire_info(srcWire).downhill.push_back(name); + wire_info(dstWire).uphill.push_back(name); pip_ids.push_back(name); if (int(tilePipDimZ.size()) <= loc.x) @@ -75,7 +99,7 @@ void Arch::addAlias(IdString name, IdString type, IdString srcWire, IdString dst pi.dstWire = dstWire; pi.delay = delay; - wires.at(srcWire).aliases.push_back(name); + wire_info(srcWire).aliases.push_back(name); pip_ids.push_back(name); } @@ -115,38 +139,38 @@ void Arch::addBel(IdString name, IdString type, Loc loc, bool gb) void Arch::addBelInput(IdString bel, IdString name, IdString wire) { - NPNR_ASSERT(bels.at(bel).pins.count(name) == 0); - PinInfo &pi = bels.at(bel).pins[name]; + NPNR_ASSERT(bel_info(bel).pins.count(name) == 0); + PinInfo &pi = bel_info(bel).pins[name]; pi.name = name; pi.wire = wire; pi.type = PORT_IN; - wires.at(wire).downhill_bel_pins.push_back(BelPin{bel, name}); - wires.at(wire).bel_pins.push_back(BelPin{bel, name}); + wire_info(wire).downhill_bel_pins.push_back(BelPin{bel, name}); + wire_info(wire).bel_pins.push_back(BelPin{bel, name}); } void Arch::addBelOutput(IdString bel, IdString name, IdString wire) { - NPNR_ASSERT(bels.at(bel).pins.count(name) == 0); - PinInfo &pi = bels.at(bel).pins[name]; + NPNR_ASSERT(bel_info(bel).pins.count(name) == 0); + PinInfo &pi = bel_info(bel).pins[name]; pi.name = name; pi.wire = wire; pi.type = PORT_OUT; - wires.at(wire).uphill_bel_pin = BelPin{bel, name}; - wires.at(wire).bel_pins.push_back(BelPin{bel, name}); + wire_info(wire).uphill_bel_pin = BelPin{bel, name}; + wire_info(wire).bel_pins.push_back(BelPin{bel, name}); } void Arch::addBelInout(IdString bel, IdString name, IdString wire) { - NPNR_ASSERT(bels.at(bel).pins.count(name) == 0); - PinInfo &pi = bels.at(bel).pins[name]; + NPNR_ASSERT(bel_info(bel).pins.count(name) == 0); + PinInfo &pi = bel_info(bel).pins[name]; pi.name = name; pi.wire = wire; pi.type = PORT_INOUT; - wires.at(wire).downhill_bel_pins.push_back(BelPin{bel, name}); - wires.at(wire).bel_pins.push_back(BelPin{bel, name}); + wire_info(wire).downhill_bel_pins.push_back(BelPin{bel, name}); + wire_info(wire).bel_pins.push_back(BelPin{bel, name}); } void Arch::addGroupBel(IdString group, IdString bel) { groups[group].bels.push_back(bel); } @@ -165,19 +189,19 @@ void Arch::addDecalGraphic(DecalId decal, const GraphicElement &graphic) void Arch::setWireDecal(WireId wire, DecalXY decalxy) { - wires.at(wire).decalxy = decalxy; + wire_info(wire).decalxy = decalxy; refreshUiWire(wire); } void Arch::setPipDecal(PipId pip, DecalXY decalxy) { - pips.at(pip).decalxy = decalxy; + pip_info(pip).decalxy = decalxy; refreshUiPip(pip); } void Arch::setBelDecal(BelId bel, DecalXY decalxy) { - bels.at(bel).decalxy = decalxy; + bel_info(bel).decalxy = decalxy; refreshUiBel(bel); } @@ -187,11 +211,11 @@ void Arch::setGroupDecal(GroupId group, DecalXY decalxy) refreshUiGroup(group); } -void Arch::setWireAttr(IdString wire, IdString key, const std::string &value) { wires.at(wire).attrs[key] = value; } +void Arch::setWireAttr(IdString wire, IdString key, const std::string &value) { wire_info(wire).attrs[key] = value; } -void Arch::setPipAttr(IdString pip, IdString key, const std::string &value) { pips.at(pip).attrs[key] = value; } +void Arch::setPipAttr(IdString pip, IdString key, const std::string &value) { pip_info(pip).attrs[key] = value; } -void Arch::setBelAttr(IdString bel, IdString key, const std::string &value) { bels.at(bel).attrs[key] = value; } +void Arch::setBelAttr(IdString bel, IdString key, const std::string &value) { bel_info(bel).attrs[key] = value; } void Arch::setLutK(int K) { args.K = K; } diff --git a/generic/arch.h b/generic/arch.h index e9d3593c..444d2636 100644 --- a/generic/arch.h +++ b/generic/arch.h @@ -122,6 +122,11 @@ struct Arch : BaseCtx std::unordered_map<IdString, BelInfo> bels; std::unordered_map<GroupId, GroupInfo> groups; + // These functions include useful errors if not found + WireInfo &wire_info(IdString wire); + PipInfo &pip_info(IdString wire); + BelInfo &bel_info(IdString wire); + std::vector<IdString> bel_ids, wire_ids, pip_ids; std::unordered_map<Loc, BelId> bel_by_loc; diff --git a/generic/examples/.gitignore b/generic/examples/.gitignore index 38e95de5..ad2fba28 100644 --- a/generic/examples/.gitignore +++ b/generic/examples/.gitignore @@ -1,3 +1,6 @@ blinky.fasm __pycache__ *.pyc +pnrblinky.v +/blinky_simtest +*.vcd diff --git a/generic/examples/blinky.v b/generic/examples/blinky.v index b7cb1b86..42becb72 100644 --- a/generic/examples/blinky.v +++ b/generic/examples/blinky.v @@ -1,9 +1,12 @@ -module top(input clk, output reg [7:0] leds); +module top(input clk, rst, output reg [7:0] leds); -reg [25:0] ctr; +reg [7:0] ctr; always @(posedge clk) - ctr <= ctr + 1'b1; + if (rst) + ctr <= 8'h00; + else + ctr <= ctr + 1'b1; -assign leds = ctr[25:18]; +assign leds = ctr; -endmodule
\ No newline at end of file +endmodule diff --git a/generic/examples/blinky_tb.v b/generic/examples/blinky_tb.v new file mode 100644 index 00000000..f9925e6f --- /dev/null +++ b/generic/examples/blinky_tb.v @@ -0,0 +1,38 @@ +`timescale 1ns / 1ps +module blinky_tb; + +reg clk = 1'b0, rst = 1'b0; +reg [7:0] ctr_gold = 8'h00; +wire [7:0] ctr_gate; +top dut_i(.clk(clk), .rst(rst), .leds(ctr_gate)); + +task oneclk; + begin + clk = 1'b1; + #10; + clk = 1'b0; + #10; + end +endtask + +initial begin + $dumpfile("blinky_simtest.vcd"); + $dumpvars(0, blinky_tb); + #100; + rst = 1'b1; + repeat (5) oneclk; + #5 + rst = 1'b0; + #5 + repeat (500) begin + if (ctr_gold !== ctr_gate) begin + $display("mismatch gold=%b gate=%b", ctr_gold, ctr_gate); + $stop; + end + oneclk; + ctr_gold = ctr_gold + 1'b1; + end + $finish; +end + +endmodule diff --git a/generic/examples/simtest.sh b/generic/examples/simtest.sh new file mode 100755 index 00000000..ef328914 --- /dev/null +++ b/generic/examples/simtest.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +set -ex +yosys -p "tcl ../synth/synth_generic.tcl 4 blinky.json" blinky.v +${NEXTPNR:-../../nextpnr-generic} --no-iobs --pre-pack simple.py --pre-place simple_timing.py --json blinky.json --post-route bitstream.py --write pnrblinky.json +yosys -p "read_json pnrblinky.json; write_verilog -noattr -norename pnrblinky.v" +iverilog -o blinky_simtest ../synth/prims.v blinky_tb.v pnrblinky.v +vvp -N ./blinky_simtest diff --git a/generic/main.cc b/generic/main.cc index 7dfc6aa7..bb780996 100644 --- a/generic/main.cc +++ b/generic/main.cc @@ -46,6 +46,7 @@ po::options_description GenericCommandHandler::getArchOptions() { po::options_description specific("Architecture specific options"); specific.add_options()("generic", "set device type to generic"); + specific.add_options()("no-iobs", "disable automatic IO buffer insertion"); return specific; } @@ -59,7 +60,10 @@ std::unique_ptr<Context> GenericCommandHandler::createContext(std::unordered_map if (arch_name != "generic") log_error("Unsuported architecture '%s'.\n", arch_name.c_str()); } - return std::unique_ptr<Context>(new Context(chipArgs)); + auto ctx = std::unique_ptr<Context>(new Context(chipArgs)); + if (vm.count("no-iobs")) + ctx->settings[ctx->id("disable_iobs")] = Property::State::S1; + return ctx; } int main(int argc, char *argv[]) diff --git a/generic/pack.cc b/generic/pack.cc index 69f248d2..19266aba 100644 --- a/generic/pack.cc +++ b/generic/pack.cc @@ -249,6 +249,10 @@ static void pack_io(Context *ctx) delete_nets.insert(net2->name); } } + } else if (bool_or_default(ctx->settings, ctx->id("disable_iobs"))) { + // No IO buffer insertion; just remove nextpnr_[io]buf + for (auto &p : ci->ports) + disconnect_port(ctx, ci, p.first); } else { // Create a GENERIC_IOB buffer std::unique_ptr<CellInfo> ice_cell = diff --git a/generic/synth/prims.v b/generic/synth/prims.v index 1148041c..ca445e6e 100644 --- a/generic/synth/prims.v +++ b/generic/synth/prims.v @@ -2,18 +2,27 @@ module LUT #( parameter K = 4, - parameter [2**K-1:0] INIT = 0, + parameter [2**K-1:0] INIT = 0 ) ( input [K-1:0] I, output Q ); - assign Q = INIT[I]; + wire [K-1:0] I_pd; + + genvar ii; + generate + for (ii = 0; ii < K; ii = ii + 1'b1) + assign I_pd[ii] = (I[ii] === 1'bz) ? 1'b0 : I[ii]; + endgenerate + + assign Q = INIT[I_pd]; endmodule module DFF ( input CLK, D, output reg Q ); + initial Q = 1'b0; always @(posedge CLK) Q <= D; endmodule diff --git a/json/jsonwrite.cc b/json/jsonwrite.cc index 3d3b70e4..9723d3b2 100644 --- a/json/jsonwrite.cc +++ b/json/jsonwrite.cc @@ -73,14 +73,17 @@ struct PortGroup PortType dir; }; -std::vector<PortGroup> group_ports(Context *ctx) +std::vector<PortGroup> group_ports(Context *ctx, const std::unordered_map<IdString, PortInfo> &ports, + bool is_cell = false) { std::vector<PortGroup> groups; std::unordered_map<std::string, size_t> base_to_group; - for (auto &pair : ctx->ports) { + for (auto &pair : ports) { std::string name = pair.second.name.str(ctx); if ((name.back() != ']') || (name.find('[') == std::string::npos)) { - groups.push_back({name, {pair.first.index}, pair.second.type}); + groups.push_back({name, + {is_cell ? (pair.second.net ? pair.second.net->name.index : -1) : pair.first.index}, + pair.second.type}); } else { int off1 = int(name.find_last_of('[')); std::string basename = name.substr(0, off1); @@ -95,26 +98,27 @@ std::vector<PortGroup> group_ports(Context *ctx) if (int(grp.bits.size()) <= index) grp.bits.resize(index + 1, -1); NPNR_ASSERT(grp.bits.at(index) == -1); - grp.bits.at(index) = pair.second.net ? pair.second.net->name.index : pair.first.index; + grp.bits.at(index) = pair.second.net ? pair.second.net->name.index : (is_cell ? -1 : pair.first.index); } } return groups; -}; +} -std::string format_port_bits(const PortGroup &port) +std::string format_port_bits(const PortGroup &port, int &dummy_idx) { std::stringstream s; s << "[ "; bool first = true; - for (auto bit : port.bits) { - if (!first) - s << ", "; - if (bit == -1) - s << "\"x\""; - else - s << bit; - first = false; - } + if (port.bits.size() != 1 || port.bits.at(0) != -1) // skip single disconnected ports + for (auto bit : port.bits) { + if (!first) + s << ", "; + if (bit == -1) + s << (++dummy_idx); + else + s << bit; + first = false; + } s << " ]"; return s.str(); } @@ -122,6 +126,7 @@ std::string format_port_bits(const PortGroup &port) void write_module(std::ostream &f, Context *ctx) { auto val = ctx->attrs.find(ctx->id("module")); + int dummy_idx = int(ctx->idstring_idx_to_str->size()) + 1000; if (val != ctx->attrs.end()) f << stringf(" %s: {\n", get_string(val->second.as_string()).c_str()); else @@ -134,14 +139,14 @@ void write_module(std::ostream &f, Context *ctx) f << stringf("\n },\n"); f << stringf(" \"ports\": {"); - auto ports = group_ports(ctx); + auto ports = group_ports(ctx, ctx->ports); bool first = true; for (auto &port : ports) { f << stringf("%s\n", first ? "" : ","); f << stringf(" %s: {\n", get_string(port.name).c_str()); f << stringf(" \"direction\": \"%s\",\n", port.dir == PORT_IN ? "input" : port.dir == PORT_INOUT ? "inout" : "output"); - f << stringf(" \"bits\": %s\n", format_port_bits(port).c_str()); + f << stringf(" \"bits\": %s\n", format_port_bits(port, dummy_idx).c_str()); f << stringf(" }"); first = false; } @@ -151,6 +156,7 @@ void write_module(std::ostream &f, Context *ctx) first = true; for (auto &pair : ctx->cells) { auto &c = pair.second; + auto cell_ports = group_ports(ctx, c->ports, true); f << stringf("%s\n", first ? "" : ","); f << stringf(" %s: {\n", get_name(c->name, ctx).c_str()); f << stringf(" \"hide_name\": %s,\n", c->name.c_str(ctx)[0] == '$' ? "1" : "0"); @@ -163,24 +169,18 @@ void write_module(std::ostream &f, Context *ctx) f << stringf("\n },\n"); f << stringf(" \"port_directions\": {"); bool first2 = true; - for (auto &conn : c->ports) { - auto &p = conn.second; - std::string direction = (p.type == PORT_IN) ? "input" : (p.type == PORT_OUT) ? "output" : "inout"; + for (auto &pg : cell_ports) { + std::string direction = (pg.dir == PORT_IN) ? "input" : (pg.dir == PORT_OUT) ? "output" : "inout"; f << stringf("%s\n", first2 ? "" : ","); - f << stringf(" %s: \"%s\"", get_name(conn.first, ctx).c_str(), direction.c_str()); + f << stringf(" %s: \"%s\"", get_string(pg.name).c_str(), direction.c_str()); first2 = false; } f << stringf("\n },\n"); f << stringf(" \"connections\": {"); first2 = true; - for (auto &conn : c->ports) { - auto &p = conn.second; + for (auto &pg : cell_ports) { f << stringf("%s\n", first2 ? "" : ","); - if (p.net) - f << stringf(" %s: [ %d ]", get_name(conn.first, ctx).c_str(), p.net->name.index); - else - f << stringf(" %s: [ ]", get_name(conn.first, ctx).c_str()); - + f << stringf(" %s: %s", get_string(pg.name).c_str(), format_port_bits(pg, dummy_idx).c_str()); first2 = false; } f << stringf("\n }\n"); |