Merge branch 'master' of gitlab.com:SymbioticEDA/nextpnr into gridapi

This commit is contained in:
Clifford Wolf 2018-07-20 17:13:26 +02:00
commit e16b4a325e
50 changed files with 911 additions and 872 deletions

View File

@ -72,21 +72,22 @@ class assertion_failure : public std::runtime_error
int line;
};
inline void except_assert_impl(bool expr, const char *message, const char *expr_str, const char *filename, int line)
NPNR_NORETURN
inline bool assert_fail_impl(const char *message, const char *expr_str, const char *filename, int line)
{
if (!expr)
throw assertion_failure(message, expr_str, filename, line);
throw assertion_failure(message, expr_str, filename, line);
}
NPNR_NORETURN
inline void assert_false_impl(std::string message, std::string filename, int line)
inline bool assert_fail_impl_str(std::string message, const char *expr_str, const char *filename, int line)
{
throw assertion_failure(message, "false", filename, line);
throw assertion_failure(message, expr_str, filename, line);
}
#define NPNR_ASSERT(cond) except_assert_impl((cond), #cond, #cond, __FILE__, __LINE__)
#define NPNR_ASSERT_MSG(cond, msg) except_assert_impl((cond), msg, #cond, __FILE__, __LINE__)
#define NPNR_ASSERT_FALSE(msg) assert_false_impl(msg, __FILE__, __LINE__)
#define NPNR_ASSERT(cond) ((void)((cond) || (assert_fail_impl(#cond, #cond, __FILE__, __LINE__))))
#define NPNR_ASSERT_MSG(cond, msg) ((void)((cond) || (assert_fail_impl(msg, #cond, __FILE__, __LINE__))))
#define NPNR_ASSERT_FALSE(msg) (assert_fail_impl(msg, "false", __FILE__, __LINE__))
#define NPNR_ASSERT_FALSE_STR(msg) (assert_fail_impl_str(msg, "false", __FILE__, __LINE__))
struct BaseCtx;
struct Context;
@ -205,9 +206,11 @@ struct PipMap
PlaceStrength strength = STRENGTH_NONE;
};
struct NetInfo
struct NetInfo : ArchNetInfo
{
IdString name;
int32_t udata;
PortRef driver;
std::vector<PortRef> users;
std::unordered_map<IdString, std::string> attrs;
@ -230,9 +233,11 @@ struct PortInfo
PortType type;
};
struct CellInfo
struct CellInfo : ArchCellInfo
{
IdString name, type;
int32_t udata;
std::unordered_map<IdString, PortInfo> ports;
std::unordered_map<IdString, std::string> attrs, params;

View File

@ -118,6 +118,16 @@ Arch::Arch(ArchArgs args) : args(args)
log_error("Unsupported ECP5 chip type.\n");
}
#endif
package_info = nullptr;
for (int i = 0; i < chip_info->num_packages; i++) {
if (args.package == chip_info->package_info[i].name.get()) {
package_info = &(chip_info->package_info[i]);
break;
}
}
if (!package_info)
log_error("Unsupported package '%s' for '%s'.\n", args.package.c_str(), getChipName().c_str());
id_trellis_slice = id("TRELLIS_SLICE");
id_clk = id("CLK");
@ -282,9 +292,28 @@ IdString Arch::getPipName(PipId pip) const
// -----------------------------------------------------------------------
BelId Arch::getPackagePinBel(const std::string &pin) const { return BelId(); }
BelId Arch::getPackagePinBel(const std::string &pin) const
{
for (int i = 0; i < package_info->num_pins; i++) {
if (package_info->pin_data[i].name.get() == pin) {
BelId bel;
bel.location = package_info->pin_data[i].abs_loc;
bel.index = package_info->pin_data[i].bel_index;
return bel;
}
}
return BelId();
}
std::string Arch::getBelPackagePin(BelId bel) const { return ""; }
std::string Arch::getBelPackagePin(BelId bel) const
{
for (int i = 0; i < package_info->num_pins; i++) {
if (package_info->pin_data[i].abs_loc == bel.location && package_info->pin_data[i].bel_index == bel.index) {
return package_info->pin_data[i].name.get();
}
}
return "";
}
// -----------------------------------------------------------------------
void Arch::estimatePosition(BelId bel, int &x, int &y, bool &gb) const

View File

@ -96,13 +96,36 @@ NPNR_PACKED_STRUCT(struct LocationTypePOD {
RelPtr<PipInfoPOD> pip_data;
});
NPNR_PACKED_STRUCT(struct PIOInfoPOD {
Location abs_loc;
int32_t bel_index;
RelPtr<char> function_name;
int16_t bank;
int16_t padding;
});
NPNR_PACKED_STRUCT(struct PackagePinPOD {
RelPtr<char> name;
Location abs_loc;
int32_t bel_index;
});
NPNR_PACKED_STRUCT(struct PackageInfoPOD {
RelPtr<char> name;
int32_t num_pins;
RelPtr<PackagePinPOD> pin_data;
});
NPNR_PACKED_STRUCT(struct ChipInfoPOD {
int32_t width, height;
int32_t num_tiles;
int32_t num_location_types;
int32_t num_packages, num_pios;
RelPtr<LocationTypePOD> locations;
RelPtr<int32_t> location_type;
RelPtr<RelPtr<char>> tiletype_names;
RelPtr<PackageInfoPOD> package_info;
RelPtr<PIOInfoPOD> pio_info;
});
#if defined(_MSC_VER)
@ -340,6 +363,7 @@ struct ArchArgs
struct Arch : BaseCtx
{
const ChipInfoPOD *chip_info;
const PackageInfoPOD *package_info;
mutable std::unordered_map<IdString, BelId> bel_by_name;
mutable std::unordered_map<IdString, WireId> wire_by_name;

View File

@ -129,6 +129,13 @@ struct DecalId
}
};
struct ArchNetInfo
{
};
struct ArchCellInfo
{
};
NEXTPNR_NAMESPACE_END
namespace std {

View File

@ -116,6 +116,14 @@ std::unique_ptr<CellInfo> create_ecp5_cell(Context *ctx, IdString type, std::str
add_port(ctx, new_cell.get(), "I", PORT_IN);
add_port(ctx, new_cell.get(), "T", PORT_IN);
add_port(ctx, new_cell.get(), "O", PORT_OUT);
} else if (type == ctx->id("LUT4")) {
new_cell->params[ctx->id("INIT")] = "0";
add_port(ctx, new_cell.get(), "A", PORT_IN);
add_port(ctx, new_cell.get(), "B", PORT_IN);
add_port(ctx, new_cell.get(), "C", PORT_IN);
add_port(ctx, new_cell.get(), "D", PORT_IN);
add_port(ctx, new_cell.get(), "Z", PORT_OUT);
} else {
log_error("unable to create ECP5 cell of type %s", type.c_str(ctx));
}
@ -169,7 +177,7 @@ void ff_to_slice(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool drive
void lut_to_slice(Context *ctx, CellInfo *lut, CellInfo *lc, int index)
{
lc->params[ctx->id("LUT" + std::to_string(index) + "_INITVAL")] = str_or_default(lc->params, ctx->id("INIT"), "0");
lc->params[ctx->id("LUT" + std::to_string(index) + "_INITVAL")] = str_or_default(lut->params, ctx->id("INIT"), "0");
replace_port(lut, ctx->id("A"), lc, ctx->id("A" + std::to_string(index)));
replace_port(lut, ctx->id("B"), lc, ctx->id("B" + std::to_string(index)));
replace_port(lut, ctx->id("C"), lc, ctx->id("C" + std::to_string(index)));

View File

@ -68,6 +68,8 @@ int main(int argc, char *argv[])
options.add_options()("45k", "set device type to LFE5U-45F");
options.add_options()("85k", "set device type to LFE5U-85F");
options.add_options()("package", po::value<std::string>(), "select device package (defaults to CABGA381)");
options.add_options()("json", po::value<std::string>(), "JSON design file to ingest");
options.add_options()("seed", po::value<int>(), "seed value for random number generator");
@ -123,8 +125,10 @@ int main(int argc, char *argv[])
args.type = ArchArgs::LFE5U_45F;
if (vm.count("85k"))
args.type = ArchArgs::LFE5U_85F;
args.package = "CABGA381";
if (vm.count("package"))
args.package = vm["package"].as<std::string>();
else
args.package = "CABGA381";
args.speed = 6;
std::unique_ptr<Context> ctx = std::unique_ptr<Context>(new Context(args));

View File

@ -71,6 +71,15 @@ class Ecp5Packer
}
}
const NetInfo *net_or_nullptr(CellInfo *cell, IdString port)
{
auto fnd = cell->ports.find(port);
if (fnd == cell->ports.end())
return nullptr;
else
return fnd->second.net;
}
// Return whether two FFs can be packed together in the same slice
bool can_pack_ffs(CellInfo *ff0, CellInfo *ff1)
{
@ -88,11 +97,11 @@ class Ecp5Packer
if (str_or_default(ff0->params, ctx->id("CLKMUX"), "CLK") !=
str_or_default(ff1->params, ctx->id("CLKMUX"), "CLK"))
return false;
if (ff0->ports.at(ctx->id("CLK")).net != ff1->ports.at(ctx->id("CLK")).net)
if (net_or_nullptr(ff0, ctx->id("CLK")) != net_or_nullptr(ff1, ctx->id("CLK")))
return false;
if (ff0->ports.at(ctx->id("CE")).net != ff1->ports.at(ctx->id("CE")).net)
if (net_or_nullptr(ff0, ctx->id("CE")) != net_or_nullptr(ff1, ctx->id("CE")))
return false;
if (ff0->ports.at(ctx->id("LSR")).net != ff1->ports.at(ctx->id("LSR")).net)
if (net_or_nullptr(ff0, ctx->id("LSR")) != net_or_nullptr(ff1, ctx->id("LSR")))
return false;
return true;
}
@ -223,8 +232,23 @@ class Ecp5Packer
} else {
log_error("TRELLIS_IO required on all top level IOs...\n");
}
packed_cells.insert(ci->name);
std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(trio->attrs, trio->attrs.begin()));
auto loc_attr = trio->attrs.find(ctx->id("LOC"));
if (loc_attr != trio->attrs.end()) {
std::string pin = loc_attr->second;
BelId pinBel = ctx->getPackagePinBel(pin);
if (pinBel == BelId()) {
log_error("IO pin '%s' constrained to pin '%s', which does not exist for package '%s'.\n",
trio->name.c_str(ctx), pin.c_str(), ctx->args.package.c_str());
} else {
log_info("pin '%s' constrained to Bel '%s'.\n", trio->name.c_str(ctx),
ctx->getBelName(pinBel).c_str(ctx));
}
trio->attrs[ctx->id("BEL")] = ctx->getBelName(pinBel).str(ctx);
}
}
}
flush_cells();
@ -275,6 +299,8 @@ class Ecp5Packer
ff_to_slice(ctx, ff, packed.get(), 0, true);
packed_cells.insert(ff->name);
sliceUsage[packed->name].ff0_used = true;
lutffPairs.erase(ci->name);
fflutPairs.erase(ff->name);
}
new_cells.push_back(std::move(packed));
@ -304,10 +330,14 @@ class Ecp5Packer
if (ff0 != lutffPairs.end()) {
ff_to_slice(ctx, ctx->cells.at(ff0->second).get(), slice.get(), 0, true);
packed_cells.insert(ff0->second);
lutffPairs.erase(lut0->name);
fflutPairs.erase(ff0->second);
}
if (ff1 != lutffPairs.end()) {
ff_to_slice(ctx, ctx->cells.at(ff1->second).get(), slice.get(), 1, true);
packed_cells.insert(ff1->second);
lutffPairs.erase(lut1->name);
fflutPairs.erase(ff1->second);
}
new_cells.push_back(std::move(slice));
@ -333,6 +363,8 @@ class Ecp5Packer
if (ff != lutffPairs.end()) {
ff_to_slice(ctx, ctx->cells.at(ff->second).get(), slice.get(), 0, true);
packed_cells.insert(ff->second);
lutffPairs.erase(ci->name);
fflutPairs.erase(ff->second);
}
new_cells.push_back(std::move(slice));
@ -359,10 +391,113 @@ class Ecp5Packer
flush_cells();
}
void set_lut_input_constant(CellInfo *cell, IdString input, bool value)
{
int index = std::string("ABCD").find(input.str(ctx));
int init = int_or_default(cell->params, ctx->id("INIT"));
int new_init = 0;
for (int i = 0; i < 16; i++) {
if (((i >> index) & 0x1) != value) {
int other_i = (i & (~(1 << index))) | (value << index);
if ((init >> other_i) & 0x1)
new_init |= (1 << i);
} else {
if ((init >> i) & 0x1)
new_init |= (1 << i);
}
}
cell->params[ctx->id("INIT")] = std::to_string(new_init);
cell->ports.at(input).net = nullptr;
}
// Merge a net into a constant net
void set_net_constant(const Context *ctx, NetInfo *orig, NetInfo *constnet, bool constval)
{
orig->driver.cell = nullptr;
for (auto user : orig->users) {
if (user.cell != nullptr) {
CellInfo *uc = user.cell;
if (ctx->verbose)
log_info("%s user %s\n", orig->name.c_str(ctx), uc->name.c_str(ctx));
if (is_lut(ctx, uc)) {
set_lut_input_constant(uc, user.port, constval);
} else if (is_ff(ctx, uc) && user.port == ctx->id("CE")) {
uc->params[ctx->id("CEMUX")] = constval ? "1" : "0";
uc->ports[user.port].net = nullptr;
} else if (is_ff(ctx, uc) && user.port == ctx->id("LSR") &&
((!constval && str_or_default(uc->params, ctx->id("LSRMUX"), "LSR") == "LSR") ||
(constval && str_or_default(uc->params, ctx->id("LSRMUX"), "LSR") == "INV"))) {
uc->ports[user.port].net = nullptr;
} else {
uc->ports[user.port].net = constnet;
constnet->users.push_back(user);
}
}
}
orig->users.clear();
}
// Pack constants (simple implementation)
void pack_constants()
{
log_info("Packing constants..\n");
std::unique_ptr<CellInfo> gnd_cell = create_ecp5_cell(ctx, ctx->id("LUT4"), "$PACKER_GND");
gnd_cell->params[ctx->id("INIT")] = "0";
std::unique_ptr<NetInfo> gnd_net = std::unique_ptr<NetInfo>(new NetInfo);
gnd_net->name = ctx->id("$PACKER_GND_NET");
gnd_net->driver.cell = gnd_cell.get();
gnd_net->driver.port = ctx->id("Z");
gnd_cell->ports.at(ctx->id("Z")).net = gnd_net.get();
std::unique_ptr<CellInfo> vcc_cell = create_ecp5_cell(ctx, ctx->id("LUT4"), "$PACKER_VCC");
vcc_cell->params[ctx->id("INIT")] = "65535";
std::unique_ptr<NetInfo> vcc_net = std::unique_ptr<NetInfo>(new NetInfo);
vcc_net->name = ctx->id("$PACKER_VCC_NET");
vcc_net->driver.cell = vcc_cell.get();
vcc_net->driver.port = ctx->id("Z");
vcc_cell->ports.at(ctx->id("Z")).net = vcc_net.get();
std::vector<IdString> dead_nets;
bool gnd_used = false, vcc_used = false;
for (auto net : sorted(ctx->nets)) {
NetInfo *ni = net.second;
if (ni->driver.cell != nullptr && ni->driver.cell->type == ctx->id("GND")) {
IdString drv_cell = ni->driver.cell->name;
set_net_constant(ctx, ni, gnd_net.get(), false);
gnd_used = true;
dead_nets.push_back(net.first);
ctx->cells.erase(drv_cell);
} else if (ni->driver.cell != nullptr && ni->driver.cell->type == ctx->id("VCC")) {
IdString drv_cell = ni->driver.cell->name;
set_net_constant(ctx, ni, vcc_net.get(), true);
vcc_used = true;
dead_nets.push_back(net.first);
ctx->cells.erase(drv_cell);
}
}
if (gnd_used) {
ctx->cells[gnd_cell->name] = std::move(gnd_cell);
ctx->nets[gnd_net->name] = std::move(gnd_net);
}
if (vcc_used) {
ctx->cells[vcc_cell->name] = std::move(vcc_cell);
ctx->nets[vcc_net->name] = std::move(vcc_net);
}
for (auto dn : dead_nets) {
ctx->nets.erase(dn);
}
}
public:
void pack()
{
pack_io();
pack_constants();
find_lutff_pairs();
pack_lut5s();
pair_luts();

View File

@ -1,46 +1,46 @@
module top(input clk_pin, input btn_pin, output [3:0] led_pin, output gpio0_pin);
module top(input clk_pin, input btn_pin, output [7:0] led_pin, output gpio0_pin);
wire clk;
wire [7:0] led;
wire clk;
wire [7:0] led;
wire btn;
wire gpio0;
(* BEL="X0/Y35/PIOA" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("INPUT")) clk_buf (.B(clk_pin), .O(clk));
(* LOC="G2" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("INPUT")) clk_buf (.B(clk_pin), .O(clk));
(* BEL="X4/Y71/PIOA" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("INPUT")) btn_buf (.B(btn_pin), .O(btn));
(* LOC="R1" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("INPUT")) btn_buf (.B(btn_pin), .O(btn));
(* BEL="X0/Y23/PIOC" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_0 (.B(led_pin[0]), .I(led[0]));
(* BEL="X0/Y23/PIOD" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_1 (.B(led_pin[1]), .I(led[1]));
(* BEL="X0/Y26/PIOA" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_2 (.B(led_pin[2]), .I(led[2]));
(* BEL="X0/Y26/PIOC" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_3 (.B(led_pin[3]), .I(led[3]));
(* LOC="B2" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_0 (.B(led_pin[0]), .I(led[0]));
(* LOC="C2" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_1 (.B(led_pin[1]), .I(led[1]));
(* LOC="C1" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_2 (.B(led_pin[2]), .I(led[2]));
(* LOC="D2" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_3 (.B(led_pin[3]), .I(led[3]));
(* BEL="X0/Y26/PIOB" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_4 (.B(led_pin[4]), .I(led[4]));
(* BEL="X0/Y32/PIOD" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_5 (.B(led_pin[5]), .I(led[5]));
(* BEL="X0/Y26/PIOD" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_6 (.B(led_pin[6]), .I(led[6]));
(* BEL="X0/Y29/PIOD" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_7 (.B(led_pin[7]), .I(led[7]));
(* LOC="D1" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_4 (.B(led_pin[4]), .I(led[4]));
(* LOC="E2" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_5 (.B(led_pin[5]), .I(led[5]));
(* LOC="E1" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_6 (.B(led_pin[6]), .I(led[6]));
(* LOC="H3" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_7 (.B(led_pin[7]), .I(led[7]));
(* BEL="X0/Y62/PIOD" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) gpio0_buf (.B(gpio0_pin), .I(gpio0));
(* LOC="L2" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) gpio0_buf (.B(gpio0_pin), .I(gpio0));
localparam ctr_width = 24;
localparam ctr_max = 2**ctr_width - 1;
reg [ctr_width-1:0] ctr = 0;
reg [9:0] pwm_ctr = 0;
reg [ctr_width-1:0] ctr = 0;
reg [9:0] pwm_ctr = 0;
reg dir = 0;
always@(posedge clk) begin
ctr <= btn ? ctr : (dir ? ctr - 1'b1 : ctr + 1'b1);
always@(posedge clk) begin
ctr <= btn ? ctr : (dir ? ctr - 1'b1 : ctr + 1'b1);
if (ctr[ctr_width-1 : ctr_width-3] == 0 && dir == 1)
dir <= 1'b0;
else if (ctr[ctr_width-1 : ctr_width-3] == 7 && dir == 0)
@ -54,24 +54,24 @@ module top(input clk_pin, input btn_pin, output [3:0] led_pin, output gpio0_pin)
genvar i;
generate
for (i = 0; i < 8; i=i+1) begin
always @ (posedge clk) begin
if (ctr[ctr_width-1 : ctr_width-3] == i)
brightness[i] <= bright_max;
else if (ctr[ctr_width-1 : ctr_width-3] == (i - 1))
brightness[i] <= ctr[ctr_width-4:ctr_width-13];
else if (ctr[ctr_width-1 : ctr_width-3] == (i + 1))
brightness[i] <= bright_max - ctr[ctr_width-4:ctr_width-13];
else
brightness[i] <= 0;
led_reg[i] <= pwm_ctr < brightness[i];
end
end
for (i = 0; i < 8; i=i+1) begin
always @ (posedge clk) begin
if (ctr[ctr_width-1 : ctr_width-3] == i)
brightness[i] <= bright_max;
else if (ctr[ctr_width-1 : ctr_width-3] == (i - 1))
brightness[i] <= ctr[ctr_width-4:ctr_width-13];
else if (ctr[ctr_width-1 : ctr_width-3] == (i + 1))
brightness[i] <= bright_max - ctr[ctr_width-4:ctr_width-13];
else
brightness[i] <= 0;
led_reg[i] <= pwm_ctr < brightness[i];
end
end
endgenerate
assign led = led_reg;
// Tie GPIO0, keep board from rebooting
TRELLIS_SLICE #(.MODE("LOGIC"), .LUT0_INITVAL(16'hFFFF)) vcc (.F0(gpio0));
assign gpio0 = 1'b1;
endmodule

View File

@ -1,9 +1,2 @@
read_verilog blinky.v
read_verilog -lib cells.v
synth -top top
abc -lut 4
techmap -map simple_map.v
splitnets
opt_clean
stat
write_json blinky.json
synth_ecp5 -noccu2 -nomux -nodram -json blinky.json

View File

@ -1,2 +0,0 @@
read_verilog blinky.v
synth_ecp5 -noccu2 -nomux -nodram -json blinky.json

View File

@ -1,49 +0,0 @@
(* blackbox *)
module TRELLIS_SLICE(
input A0, B0, C0, D0,
input A1, B1, C1, D1,
input M0, M1,
input FCI, FXA, FXB,
input CLK, LSR, CE,
input DI0, DI1,
input WD0, WD1,
input WAD0, WAD1, WAD2, WAD3,
input WRE, WCK,
output F0, Q0,
output F1, Q1,
output FCO, OFX0, OFX1,
output WDO0, WDO1, WDO2, WDO3,
output WADO0, WADO1, WADO2, WADO3
);
parameter MODE = "LOGIC";
parameter GSR = "ENABLED";
parameter SRMODE = "LSR_OVER_CE";
parameter CEMUX = "1";
parameter CLKMUX = "CLK";
parameter LSRMUX = "LSR";
parameter LUT0_INITVAL = 16'h0000;
parameter LUT1_INITVAL = 16'h0000;
parameter REG0_SD = "0";
parameter REG1_SD = "0";
parameter REG0_REGSET = "RESET";
parameter REG1_REGSET = "RESET";
parameter CCU2_INJECT1_0 = "NO";
parameter CCU2_INJECT1_1 = "NO";
endmodule
(* blackbox *) (* keep *)
module TRELLIS_IO(
inout B,
input I,
input T,
output O,
);
parameter DIR = "INPUT";
endmodule

View File

@ -1,68 +0,0 @@
module \$_DFF_P_ (input D, C, output Q);
TRELLIS_SLICE #(
.MODE("LOGIC"),
.CLKMUX("CLK"),
.CEMUX("1"),
.REG0_SD("0"),
.REG0_REGSET("RESET"),
.SRMODE("LSR_OVER_CE"),
.GSR("DISABLED")
) _TECHMAP_REPLACE_ (
.CLK(C),
.M0(D),
.Q0(Q)
);
endmodule
module \$lut (A, Y);
parameter WIDTH = 0;
parameter LUT = 0;
input [WIDTH-1:0] A;
output Y;
generate
if (WIDTH == 1) begin
TRELLIS_SLICE #(
.MODE("LOGIC"),
.LUT0_INITVAL({8{LUT[1:0]}})
) _TECHMAP_REPLACE_ (
.A0(A[0]),
.F0(Y)
);
end
if (WIDTH == 2) begin
TRELLIS_SLICE #(
.MODE("LOGIC"),
.LUT0_INITVAL({4{LUT[3:0]}})
) _TECHMAP_REPLACE_ (
.A0(A[0]),
.B0(A[1]),
.F0(Y)
);
end
if (WIDTH == 3) begin
TRELLIS_SLICE #(
.MODE("LOGIC"),
.LUT0_INITVAL({2{LUT[7:0]}})
) _TECHMAP_REPLACE_ (
.A0(A[0]),
.B0(A[1]),
.C0(A[2]),
.F0(Y)
);
end
if (WIDTH == 4) begin
TRELLIS_SLICE #(
.MODE("LOGIC"),
.LUT0_INITVAL(LUT)
) _TECHMAP_REPLACE_ (
.A0(A[0]),
.B0(A[1]),
.C0(A[2]),
.D0(A[3]),
.F0(Y)
);
end
endgenerate
endmodule

View File

@ -1,18 +0,0 @@
module top(input a_pin, output led_pin, output led2_pin, output gpio0_pin);
wire a;
wire led, led2;
wire gpio0;
(* BEL="X4/Y71/PIOA" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("INPUT")) a_buf (.B(a_pin), .O(a));
(* BEL="X0/Y23/PIOC" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) led_buf (.B(led_pin), .I(led));
(* BEL="X0/Y26/PIOA" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) led2_buf (.B(led2_pin), .I(led2));
(* BEL="X0/Y62/PIOD" *) (* IO_TYPE="LVCMOS33" *)
TRELLIS_IO #(.DIR("OUTPUT")) gpio0_buf (.B(gpio0_pin), .I(gpio0));
assign led = a;
assign led2 = !a;
TRELLIS_SLICE #(.MODE("LOGIC"), .LUT0_INITVAL(16'hFFFF)) vcc (.F0(gpio0));
endmodule

View File

@ -1,9 +0,0 @@
read_verilog ulx3s.v
read_verilog -lib cells.v
synth -top top
abc -lut 4
techmap -map simple_map.v
splitnets
opt_clean
stat
write_json ulx3s.json

View File

@ -1,11 +0,0 @@
module top(input a_pin, output [3:0] led_pin);
wire a;
wire [3:0] led;
TRELLIS_IO #(.DIR("INPUT")) a_buf (.B(a_pin), .O(a));
TRELLIS_IO #(.DIR("OUTPUT")) led_buf [3:0] (.B(led_pin), .I(led));
//assign led[0] = !a;
always @(posedge a) led[0] <= !led[0];
endmodule

View File

@ -1,9 +0,0 @@
read_verilog wire.v
read_verilog -lib cells.v
synth -top top
abc -lut 4
techmap -map simple_map.v
splitnets
opt_clean
stat
write_json wire.json

View File

@ -2,6 +2,8 @@
import pytrellis
import database
import argparse
import json
from os import path
location_types = dict()
type_at_location = dict()
@ -319,6 +321,49 @@ bel_types = {
"PIO": 2
}
def get_bel_index(ddrg, loc, name):
loctype = ddrg.locationTypes[ddrg.typeAtLocation[loc]]
idx = 0
for bel in loctype.bels:
if ddrg.to_str(bel.name) == name:
return idx
idx += 1
assert loc.y == max_row # Only missing IO should be special pins at bottom of device
return None
packages = {}
pindata = []
def process_pio_db(ddrg, device):
piofile = path.join(database.get_db_root(), "ECP5", dev_names[device], "iodb.json")
with open(piofile, 'r') as f:
piodb = json.load(f)
for pkgname, pkgdata in sorted(piodb["packages"].items()):
pins = []
for name, pinloc in sorted(pkgdata.items()):
x = pinloc["col"]
y = pinloc["row"]
loc = pytrellis.Location(x, y)
pio = "PIO" + pinloc["pio"]
bel_idx = get_bel_index(ddrg, loc, pio)
if bel_idx is not None:
pins.append((name, loc, bel_idx))
packages[pkgname] = pins
for metaitem in piodb["pio_metadata"]:
x = metaitem["col"]
y = metaitem["row"]
loc = pytrellis.Location(x, y)
pio = "PIO" + metaitem["pio"]
bank = metaitem["bank"]
if "function" in metaitem:
pinfunc = metaitem["function"]
else:
pinfunc = None
bel_idx = get_bel_index(ddrg, loc, pio)
if bel_idx is not None:
pindata.append((loc, bel_idx, bank, pinfunc))
def write_database(dev_name, ddrg, endianness):
def write_loc(loc, sym_name):
@ -409,6 +454,32 @@ def write_database(dev_name, ddrg, endianness):
for y in range(0, max_row+1):
for x in range(0, max_col+1):
bba.u32(loctypes.index(ddrg.typeAtLocation[pytrellis.Location(x, y)]), "loctype")
for package, pkgdata in sorted(packages.items()):
bba.l("package_data_%s" % package, "PackagePinPOD")
for pin in pkgdata:
name, loc, bel_idx = pin
bba.s(name, "name")
write_loc(loc, "abs_loc")
bba.u32(bel_idx, "bel_index")
bba.l("package_data", "PackageInfoPOD")
for package, pkgdata in sorted(packages.items()):
bba.s(package, "name")
bba.u32(len(pkgdata), "num_pins")
bba.r("package_data_%s" % package, "pin_data")
bba.l("pio_info", "PIOInfoPOD")
for pin in pindata:
loc, bel_idx, bank, func = pin
write_loc(loc, "abs_loc")
bba.u32(bel_idx, "bel_index")
if func is not None:
bba.s(func, "function_name")
else:
bba.r(None, "function_name")
bba.u16(bank, "bank")
bba.u16(0, "padding")
bba.l("tiletype_names", "RelPtr<char>")
for tt in tiletype_names:
@ -419,9 +490,15 @@ def write_database(dev_name, ddrg, endianness):
bba.u32(max_row + 1, "height")
bba.u32((max_col + 1) * (max_row + 1), "num_tiles")
bba.u32(len(location_types), "num_location_types")
bba.u32(len(packages), "num_packages")
bba.u32(len(pindata), "num_pios")
bba.r("locations", "locations")
bba.r("location_types", "location_type")
bba.r("tiletype_names", "tiletype_names")
bba.r("package_data", "package_info")
bba.r("pio_info", "pio_info")
bba.finalize()
return bba
@ -451,6 +528,7 @@ def main():
ddrg = pytrellis.make_dedup_chipdb(chip)
max_row = chip.get_max_row()
max_col = chip.get_max_col()
process_pio_db(ddrg, args.device)
print("{} unique location types".format(len(ddrg.locationTypes)))
bba = write_database(args.device, ddrg, "le")

View File

@ -52,4 +52,11 @@ typedef IdString PipId;
typedef IdString GroupId;
typedef IdString DecalId;
struct ArchNetInfo
{
};
struct ArchCellInfo
{
};
NEXTPNR_NAMESPACE_END

View File

@ -9,6 +9,6 @@
<file>resources/resultset_previous.png</file>
<file>resources/resultset_next.png</file>
<file>resources/resultset_last.png</file>
<file>resources/splash.png</file>
<file>resources/cross.png</file>
</qresource>
</RCC>

View File

@ -29,7 +29,6 @@
#include "log.h"
#include "mainwindow.h"
#include "pythontab.h"
#include "yosystab.h"
static void initBasenameResource() { Q_INIT_RESOURCE(base); }
@ -95,29 +94,12 @@ BaseMainWindow::BaseMainWindow(std::unique_ptr<Context> context, QWidget *parent
splitter_v->addWidget(centralTabWidget);
splitter_v->addWidget(tabWidget);
displaySplash();
}
BaseMainWindow::~BaseMainWindow() {}
void BaseMainWindow::closeTab(int index) { delete centralTabWidget->widget(index); }
void BaseMainWindow::displaySplash()
{
splash = new QSplashScreen();
splash->setPixmap(QPixmap(":/icons/resources/splash.png"));
splash->show();
connect(designview, SIGNAL(finishContextLoad()), splash, SLOT(close()));
connect(designview, SIGNAL(contextLoadStatus(std::string)), this, SLOT(displaySplashMessage(std::string)));
QCoreApplication::instance()->processEvents();
}
void BaseMainWindow::displaySplashMessage(std::string msg)
{
splash->showMessage(msg.c_str(), Qt::AlignCenter | Qt::AlignBottom, Qt::white);
QCoreApplication::instance()->processEvents();
}
void BaseMainWindow::writeInfo(std::string text) { console->info(text); }
void BaseMainWindow::createMenusAndBars()
@ -147,10 +129,6 @@ void BaseMainWindow::createMenusAndBars()
actionExit->setStatusTip("Exit the application");
connect(actionExit, SIGNAL(triggered()), this, SLOT(close()));
QAction *actionYosys = new QAction("Yosys", this);
actionYosys->setStatusTip("Run Yosys");
connect(actionYosys, SIGNAL(triggered()), this, SLOT(yosys()));
QAction *actionAbout = new QAction("About", this);
menuBar = new QMenuBar();
@ -183,19 +161,6 @@ void BaseMainWindow::createMenusAndBars()
mainToolBar->addAction(actionNew);
mainToolBar->addAction(actionOpen);
mainToolBar->addAction(actionSave);
mainToolBar->addAction(actionYosys);
}
void BaseMainWindow::yosys()
{
QString folder = QFileDialog::getExistingDirectory(0, ("Select Work Folder"), QDir::currentPath(),
QFileDialog::ShowDirsOnly);
if (!folder.isEmpty() && !folder.isNull()) {
YosysTab *yosysTab = new YosysTab(folder);
yosysTab->setAttribute(Qt::WA_DeleteOnClose);
centralTabWidget->addTab(yosysTab, "Yosys");
centralTabWidget->setCurrentWidget(yosysTab);
centralTabWidget->setTabToolTip(centralTabWidget->indexOf(yosysTab), folder);
}
}
NEXTPNR_NAMESPACE_END

View File

@ -26,7 +26,6 @@
#include <QMenu>
#include <QMenuBar>
#include <QProgressBar>
#include <QSplashScreen>
#include <QStatusBar>
#include <QTabWidget>
#include <QToolBar>
@ -50,17 +49,14 @@ class BaseMainWindow : public QMainWindow
protected:
void createMenusAndBars();
void displaySplash();
protected Q_SLOTS:
void writeInfo(std::string text);
void displaySplashMessage(std::string msg);
void closeTab(int index);
virtual void new_proj() = 0;
virtual void open_proj() = 0;
virtual bool save_proj() = 0;
void yosys();
Q_SIGNALS:
void contextChanged(Context *ctx);
@ -78,7 +74,6 @@ class BaseMainWindow : public QMainWindow
QAction *actionNew;
QAction *actionOpen;
QProgressBar *progressBar;
QSplashScreen *splash;
DesignWidget *designview;
};

View File

@ -125,11 +125,27 @@ DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), net
updateButtons();
});
actionClear = new QAction("", this);
actionClear->setIcon(QIcon(":/icons/resources/cross.png"));
actionClear->setEnabled(true);
connect(actionClear, &QAction::triggered, this, [this] {
history_index = -1;
history.clear();
QTreeWidgetItem *clickItem = treeWidget->selectedItems().at(0);
if (clickItem->parent()) {
ElementType type = static_cast<ElementTreeItem *>(clickItem)->getType();
if (type != ElementType::NONE)
addToHistory(treeWidget->selectedItems().at(0));
}
updateButtons();
});
QToolBar *toolbar = new QToolBar();
toolbar->addAction(actionFirst);
toolbar->addAction(actionPrev);
toolbar->addAction(actionNext);
toolbar->addAction(actionLast);
toolbar->addAction(actionClear);
QWidget *topWidget = new QWidget();
QVBoxLayout *vbox1 = new QVBoxLayout();
@ -230,7 +246,6 @@ void DesignWidget::newContext(Context *ctx)
bel_root->setText(0, "Bels");
treeWidget->insertTopLevelItem(0, bel_root);
if (ctx) {
Q_EMIT contextLoadStatus("Configuring bels...");
for (auto bel : ctx->getBels()) {
auto id = ctx->getBelName(bel);
QStringList items = QString(id.c_str(ctx)).split("/");
@ -263,7 +278,6 @@ void DesignWidget::newContext(Context *ctx)
wire_root->setText(0, "Wires");
treeWidget->insertTopLevelItem(0, wire_root);
if (ctx) {
Q_EMIT contextLoadStatus("Configuring wires...");
for (auto wire : ctx->getWires()) {
auto id = ctx->getWireName(wire);
QStringList items = QString(id.c_str(ctx)).split("/");
@ -295,7 +309,6 @@ void DesignWidget::newContext(Context *ctx)
pip_root->setText(0, "Pips");
treeWidget->insertTopLevelItem(0, pip_root);
if (ctx) {
Q_EMIT contextLoadStatus("Configuring pips...");
for (auto pip : ctx->getPips()) {
auto id = ctx->getPipName(pip);
QStringList items = QString(id.c_str(ctx)).split("/");
@ -321,18 +334,7 @@ void DesignWidget::newContext(Context *ctx)
for (auto pip : nameToItem[2].toStdMap()) {
pip_root->addChild(pip.second);
}
// Add nets to tree
nets_root = new QTreeWidgetItem(treeWidget);
nets_root->setText(0, "Nets");
treeWidget->insertTopLevelItem(0, nets_root);
// Add cells to tree
cells_root = new QTreeWidgetItem(treeWidget);
cells_root->setText(0, "Cells");
treeWidget->insertTopLevelItem(0, cells_root);
Q_EMIT finishContextLoad();
updateTree();
}
void DesignWidget::updateTree()
@ -477,13 +479,12 @@ void DesignWidget::onItemSelectionChanged()
addToHistory(clickItem);
clearProperties();
IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
Q_EMIT selected(getDecals(type, c));
if (type == ElementType::BEL) {
IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
BelId bel = ctx->getBelByName(c);
decals.push_back(ctx->getBelDecal(bel));
Q_EMIT selected(decals);
QtProperty *topItem = addTopLevelProperty("Bel");
addProperty(topItem, QVariant::String, "Name", c.c_str(ctx));
@ -494,12 +495,7 @@ void DesignWidget::onItemSelectionChanged()
ElementType::CELL);
} else if (type == ElementType::WIRE) {
IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
WireId wire = ctx->getWireByName(c);
decals.push_back(ctx->getWireDecal(wire));
Q_EMIT selected(decals);
QtProperty *topItem = addTopLevelProperty("Wire");
addProperty(topItem, QVariant::String, "Name", c.c_str(ctx));
@ -551,12 +547,7 @@ void DesignWidget::onItemSelectionChanged()
}
}
} else if (type == ElementType::PIP) {
IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
PipId pip = ctx->getPipByName(c);
decals.push_back(ctx->getPipDecal(pip));
Q_EMIT selected(decals);
QtProperty *topItem = addTopLevelProperty("Pip");
addProperty(topItem, QVariant::String, "Name", c.c_str(ctx));
@ -576,7 +567,6 @@ void DesignWidget::onItemSelectionChanged()
addProperty(delayItem, QVariant::Double, "Fall", delay.fallDelay());
addProperty(delayItem, QVariant::Double, "Average", delay.avgDelay());
} else if (type == ElementType::NET) {
IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
NetInfo *net = ctx->nets.at(c).get();
QtProperty *topItem = addTopLevelProperty("Net");
@ -613,7 +603,7 @@ void DesignWidget::onItemSelectionChanged()
auto name = ctx->getWireName(item.first).c_str(ctx);
QtProperty *wireItem = addSubGroup(wiresItem, name);
addProperty(wireItem, QVariant::String, "Name", name);
addProperty(wireItem, QVariant::String, "Wire", name, ElementType::WIRE);
if (item.second.pip != PipId())
addProperty(wireItem, QVariant::String, "Pip", ctx->getPipName(item.second.pip).c_str(ctx),
@ -625,7 +615,6 @@ void DesignWidget::onItemSelectionChanged()
}
} else if (type == ElementType::CELL) {
IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
CellInfo *cell = ctx->cells.at(c).get();
QtProperty *topItem = addTopLevelProperty("Cell");
@ -689,19 +678,28 @@ std::vector<DecalXY> DesignWidget::getDecals(ElementType type, IdString value)
WireId wire = ctx->getWireByName(value);
if (wire != WireId()) {
decals.push_back(ctx->getWireDecal(wire));
Q_EMIT selected(decals);
}
} break;
case ElementType::PIP: {
PipId pip = ctx->getPipByName(value);
if (pip != PipId()) {
decals.push_back(ctx->getPipDecal(pip));
Q_EMIT selected(decals);
}
} break;
case ElementType::NET: {
NetInfo *net = ctx->nets.at(value).get();
for (auto &item : net->wires) {
decals.push_back(ctx->getWireDecal(item.first));
if (item.second.pip != PipId()) {
decals.push_back(ctx->getPipDecal(item.second.pip));
}
}
} break;
case ElementType::CELL: {
CellInfo *cell = ctx->cells.at(value).get();
if (cell->bel != BelId()) {
decals.push_back(ctx->getBelDecal(cell->bel));
}
} break;
default:
break;

View File

@ -65,8 +65,6 @@ class DesignWidget : public QWidget
void info(std::string text);
void selected(std::vector<DecalXY> decal);
void highlight(std::vector<DecalXY> decal, int group);
void finishContextLoad();
void contextLoadStatus(std::string text);
private Q_SLOTS:
void prepareMenuProperty(const QPoint &pos);
@ -104,6 +102,7 @@ class DesignWidget : public QWidget
QAction *actionPrev;
QAction *actionNext;
QAction *actionLast;
QAction *actionClear;
QColor highlightColors[8];
QMap<QTreeWidgetItem *, int> highlightSelected;

View File

@ -226,7 +226,6 @@ void MainWindow::new_proj()
ctx = std::unique_ptr<Context>(new Context(chipArgs));
actionLoadJSON->setEnabled(true);
Q_EMIT displaySplash();
Q_EMIT contextChanged(ctx.get());
}
}

View File

@ -1,58 +0,0 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
*
* 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 "infotab.h"
#include <QGridLayout>
NEXTPNR_NAMESPACE_BEGIN
InfoTab::InfoTab(QWidget *parent) : QWidget(parent)
{
plainTextEdit = new QPlainTextEdit();
plainTextEdit->setReadOnly(true);
QFont f("unexistent");
f.setStyleHint(QFont::Monospace);
plainTextEdit->setFont(f);
plainTextEdit->setContextMenuPolicy(Qt::CustomContextMenu);
QAction *clearAction = new QAction("Clear &buffer", this);
clearAction->setStatusTip("Clears display buffer");
connect(clearAction, SIGNAL(triggered()), this, SLOT(clearBuffer()));
contextMenu = plainTextEdit->createStandardContextMenu();
contextMenu->addSeparator();
contextMenu->addAction(clearAction);
connect(plainTextEdit, SIGNAL(customContextMenuRequested(const QPoint)), this, SLOT(showContextMenu(const QPoint)));
QGridLayout *mainLayout = new QGridLayout();
mainLayout->addWidget(plainTextEdit);
setLayout(mainLayout);
}
void InfoTab::info(std::string str)
{
plainTextEdit->moveCursor(QTextCursor::End);
plainTextEdit->insertPlainText(str.c_str());
plainTextEdit->moveCursor(QTextCursor::End);
}
void InfoTab::showContextMenu(const QPoint &pt) { contextMenu->exec(mapToGlobal(pt)); }
void InfoTab::clearBuffer() { plainTextEdit->clear(); }
NEXTPNR_NAMESPACE_END

View File

@ -1,48 +0,0 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
*
* 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 INFOTAB_H
#define INFOTAB_H
#include <QMenu>
#include <QPlainTextEdit>
#include "nextpnr.h"
NEXTPNR_NAMESPACE_BEGIN
class InfoTab : public QWidget
{
Q_OBJECT
public:
explicit InfoTab(QWidget *parent = 0);
void info(std::string str);
public Q_SLOTS:
void clearBuffer();
private Q_SLOTS:
void showContextMenu(const QPoint &pt);
private:
QPlainTextEdit *plainTextEdit;
QMenu *contextMenu;
};
NEXTPNR_NAMESPACE_END
#endif // INFOTAB_H

BIN
gui/resources/cross.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 655 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -1,96 +0,0 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
* Copyright (C) 2018 Alex Tsui
*
* 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 "yosys_edit.h"
#include <QKeyEvent>
#include <QToolTip>
NEXTPNR_NAMESPACE_BEGIN
YosysLineEditor::YosysLineEditor(QWidget *parent) : QLineEdit(parent), index(0)
{
setContextMenuPolicy(Qt::CustomContextMenu);
QAction *clearAction = new QAction("Clear &history", this);
clearAction->setStatusTip("Clears line edit history");
connect(clearAction, SIGNAL(triggered()), this, SLOT(clearHistory()));
contextMenu = createStandardContextMenu();
contextMenu->addSeparator();
contextMenu->addAction(clearAction);
connect(this, SIGNAL(returnPressed()), SLOT(textInserted()));
connect(this, SIGNAL(customContextMenuRequested(const QPoint)), this, SLOT(showContextMenu(const QPoint)));
}
void YosysLineEditor::keyPressEvent(QKeyEvent *ev)
{
if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) {
QToolTip::hideText();
if (lines.empty())
return;
if (ev->key() == Qt::Key_Up)
index--;
if (ev->key() == Qt::Key_Down)
index++;
if (index < 0)
index = 0;
if (index >= lines.size()) {
index = lines.size();
clear();
return;
}
setText(lines[index]);
} else if (ev->key() == Qt::Key_Escape) {
QToolTip::hideText();
clear();
return;
} else if (ev->key() == Qt::Key_Tab) {
return;
}
QToolTip::hideText();
QLineEdit::keyPressEvent(ev);
}
// This makes TAB work
bool YosysLineEditor::focusNextPrevChild(bool next) { return false; }
void YosysLineEditor::textInserted()
{
if (lines.empty() || lines.back() != text())
lines += text();
if (lines.size() > 100)
lines.removeFirst();
index = lines.size();
clear();
Q_EMIT textLineInserted(lines.back());
}
void YosysLineEditor::showContextMenu(const QPoint &pt) { contextMenu->exec(mapToGlobal(pt)); }
void YosysLineEditor::clearHistory()
{
lines.clear();
index = 0;
clear();
}
NEXTPNR_NAMESPACE_END

View File

@ -1,57 +0,0 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
* Copyright (C) 2018 Alex Tsui
*
* 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 YOSYS_EDIT_H
#define YOSYS_EDIT_H
#include <QLineEdit>
#include <QMenu>
#include "nextpnr.h"
NEXTPNR_NAMESPACE_BEGIN
class YosysLineEditor : public QLineEdit
{
Q_OBJECT
public:
explicit YosysLineEditor(QWidget *parent = 0);
private Q_SLOTS:
void textInserted();
void showContextMenu(const QPoint &pt);
void clearHistory();
Q_SIGNALS:
void textLineInserted(QString);
protected:
void keyPressEvent(QKeyEvent *) Q_DECL_OVERRIDE;
bool focusNextPrevChild(bool next) Q_DECL_OVERRIDE;
private:
int index;
QStringList lines;
QMenu *contextMenu;
};
NEXTPNR_NAMESPACE_END
#endif // YOSYS_EDIT_H

View File

@ -1,108 +0,0 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
*
* 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 "yosystab.h"
#include <QGridLayout>
#include <QMessageBox>
NEXTPNR_NAMESPACE_BEGIN
YosysTab::YosysTab(QString folder, QWidget *parent) : QWidget(parent)
{
QFont f("unexistent");
f.setStyleHint(QFont::Monospace);
console = new QPlainTextEdit();
console->setMinimumHeight(100);
console->setReadOnly(true);
console->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
console->setFont(f);
console->setContextMenuPolicy(Qt::CustomContextMenu);
QAction *clearAction = new QAction("Clear &buffer", this);
clearAction->setStatusTip("Clears display buffer");
connect(clearAction, SIGNAL(triggered()), this, SLOT(clearBuffer()));
contextMenu = console->createStandardContextMenu();
contextMenu->addSeparator();
contextMenu->addAction(clearAction);
connect(console, SIGNAL(customContextMenuRequested(const QPoint)), this, SLOT(showContextMenu(const QPoint)));
lineEdit = new YosysLineEditor();
lineEdit->setMinimumHeight(30);
lineEdit->setMaximumHeight(30);
lineEdit->setFont(f);
lineEdit->setFocus();
lineEdit->setEnabled(false);
lineEdit->setPlaceholderText("yosys>");
connect(lineEdit, SIGNAL(textLineInserted(QString)), this, SLOT(editLineReturnPressed(QString)));
QGridLayout *mainLayout = new QGridLayout();
mainLayout->addWidget(console, 0, 0);
mainLayout->addWidget(lineEdit, 1, 0);
setLayout(mainLayout);
process = new QProcess();
connect(process, SIGNAL(readyReadStandardError()), this, SLOT(onReadyReadStandardError()));
connect(process, SIGNAL(readyReadStandardOutput()), this, SLOT(onReadyReadStandardOutput()));
connect(process, &QProcess::started, this, [this] { lineEdit->setEnabled(true); });
/*
#if QT_VERSION < QT_VERSION_CHECK(5, 6, 0)
connect(process, &QProcess::error, this, [this](QProcess::ProcessError error) {
#else
connect(process, &QProcess::errorOccurred, this, [this](QProcess::ProcessError error) {
#endif
if (error == QProcess::FailedToStart) {
QMessageBox::critical(
this, QString::fromUtf8("Yosys cannot be started!"),
QString::fromUtf8("<p>Please make sure you have Yosys installed and available in path</p>"));
Q_EMIT deleteLater();
}
});
*/
process->setWorkingDirectory(folder);
process->start("yosys");
}
YosysTab::~YosysTab()
{
process->terminate();
process->waitForFinished(1000); // in ms
process->kill();
process->close();
}
void YosysTab::displayString(QString text)
{
QTextCursor cursor = console->textCursor();
cursor.movePosition(QTextCursor::End);
cursor.insertText(text);
cursor.movePosition(QTextCursor::End);
console->setTextCursor(cursor);
}
void YosysTab::onReadyReadStandardOutput() { displayString(process->readAllStandardOutput()); }
void YosysTab::onReadyReadStandardError() { displayString(process->readAllStandardError()); }
void YosysTab::editLineReturnPressed(QString text) { process->write(text.toLatin1() + "\n"); }
void YosysTab::showContextMenu(const QPoint &pt) { contextMenu->exec(mapToGlobal(pt)); }
void YosysTab::clearBuffer() { console->clear(); }
NEXTPNR_NAMESPACE_END

View File

@ -1,59 +0,0 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
*
* 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 YOSYSTAB_H
#define YOSYSTAB_H
#include <QLineEdit>
#include <QMenu>
#include <QPlainTextEdit>
#include <QProcess>
#include "nextpnr.h"
#include "yosys_edit.h"
NEXTPNR_NAMESPACE_BEGIN
class YosysTab : public QWidget
{
Q_OBJECT
public:
explicit YosysTab(QString folder, QWidget *parent = 0);
~YosysTab();
private:
void displayString(QString text);
private Q_SLOTS:
void showContextMenu(const QPoint &pt);
void editLineReturnPressed(QString text);
void onReadyReadStandardOutput();
void onReadyReadStandardError();
public Q_SLOTS:
void clearBuffer();
private:
QPlainTextEdit *console;
YosysLineEditor *lineEdit;
QMenu *contextMenu;
QProcess *process;
};
NEXTPNR_NAMESPACE_END
#endif // YOSYSTAB_H

View File

@ -25,7 +25,7 @@
#include "placer1.h"
#include "router1.h"
#include "util.h"
#include "cells.h"
NEXTPNR_NAMESPACE_BEGIN
// -----------------------------------------------------------------------
@ -44,8 +44,8 @@ IdString Arch::belTypeToId(BelType type) const
return id("ICESTORM_PLL");
if (type == TYPE_SB_WARMBOOT)
return id("SB_WARMBOOT");
if (type == TYPE_SB_MAC16)
return id("SB_MAC16");
if (type == TYPE_ICESTORM_DSP)
return id("ICESTORM_DSP");
if (type == TYPE_ICESTORM_HFOSC)
return id("ICESTORM_HFOSC");
if (type == TYPE_ICESTORM_LFOSC)
@ -79,8 +79,8 @@ BelType Arch::belTypeFromId(IdString type) const
return TYPE_ICESTORM_PLL;
if (type == id("SB_WARMBOOT"))
return TYPE_SB_WARMBOOT;
if (type == id("SB_MAC16"))
return TYPE_SB_MAC16;
if (type == id("ICESTORM_DSP"))
return TYPE_ICESTORM_DSP;
if (type == id("ICESTORM_HFOSC"))
return TYPE_ICESTORM_HFOSC;
if (type == id("ICESTORM_LFOSC"))
@ -610,8 +610,7 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
}
if (bel_type == TYPE_ICESTORM_RAM) {
for (int i = 0; i < 2; i++)
{
for (int i = 0; i < 2; i++) {
int tx = chip_info->bel_data[bel.index].x;
int ty = chip_info->bel_data[bel.index].y + i;
@ -621,7 +620,7 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
el.x1 = chip_info->bel_data[bel.index].x + logic_cell_x1;
el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2;
el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1;
el.y2 = chip_info->bel_data[bel.index].y + logic_cell_y2 + 7*logic_cell_pitch;
el.y2 = chip_info->bel_data[bel.index].y + logic_cell_y2 + 7 * logic_cell_pitch;
el.z = 0;
ret.push_back(el);
@ -698,4 +697,47 @@ bool Arch::isGlobalNet(const NetInfo *net) const
return net->driver.cell != nullptr && net->driver.port == id_glb_buf_out;
}
// Assign arch arg info
void Arch::assignArchInfo()
{
for (auto &net : getCtx()->nets) {
NetInfo *ni = net.second.get();
if (isGlobalNet(ni))
ni->is_global = true;
ni->is_enable = false;
ni->is_reset = false;
for (auto usr : ni->users) {
if (is_enable_port(this, usr))
ni->is_enable = true;
if (is_reset_port(this, usr))
ni->is_reset = true;
}
}
for (auto &cell : getCtx()->cells) {
CellInfo *ci = cell.second.get();
assignCellInfo(ci);
}
}
void Arch::assignCellInfo(CellInfo *cell)
{
cell->belType = belTypeFromId(cell->type);
if (cell->type == id_icestorm_lc) {
cell->lcInfo.dffEnable = bool_or_default(cell->params, id_dff_en);
cell->lcInfo.negClk = bool_or_default(cell->params, id_neg_clk);
cell->lcInfo.clk = get_net_or_empty(cell, id_clk);
cell->lcInfo.cen = get_net_or_empty(cell, id_cen);
cell->lcInfo.sr = get_net_or_empty(cell, id_sr);
cell->lcInfo.inputCount = 0;
if (get_net_or_empty(cell, id_i0))
cell->lcInfo.inputCount++;
if (get_net_or_empty(cell, id_i1))
cell->lcInfo.inputCount++;
if (get_net_or_empty(cell, id_i2))
cell->lcInfo.inputCount++;
if (get_net_or_empty(cell, id_i3))
cell->lcInfo.inputCount++;
}
}
NEXTPNR_NAMESPACE_END

View File

@ -153,15 +153,31 @@ NPNR_PACKED_STRUCT(struct BitstreamInfoPOD {
RelPtr<IerenInfoPOD> ierens;
});
NPNR_PACKED_STRUCT(struct BelConfigEntryPOD {
RelPtr<char> entry_name;
RelPtr<char> cbit_name;
int8_t x, y;
int16_t padding;
});
// Stores mapping between bel parameters and config bits,
// for extra cells where this mapping is non-trivial
NPNR_PACKED_STRUCT(struct BelConfigPOD {
int32_t bel_index;
int32_t num_entries;
RelPtr<BelConfigEntryPOD> entries;
});
NPNR_PACKED_STRUCT(struct ChipInfoPOD {
int32_t width, height;
int32_t num_bels, num_wires, num_pips;
int32_t num_switches, num_packages;
int32_t num_switches, num_belcfgs, num_packages;
RelPtr<BelInfoPOD> bel_data;
RelPtr<WireInfoPOD> wire_data;
RelPtr<PipInfoPOD> pip_data;
RelPtr<TileType> tile_grid;
RelPtr<BitstreamInfoPOD> bits_info;
RelPtr<BelConfigPOD> bel_config;
RelPtr<PackageInfoPOD> packages_data;
});
@ -732,6 +748,12 @@ struct Arch : BaseCtx
// Helper function for above
bool logicCellsCompatible(const std::vector<const CellInfo *> &cells) const;
// -------------------------------------------------
// Assign architecure-specific arguments to nets and cells, which must be called between packing or further
// netlist modifications, and validity checks
void assignArchInfo();
void assignCellInfo(CellInfo *cell);
IdString id_glb_buf_out;
IdString id_icestorm_lc, id_sb_io, id_sb_gb;
IdString id_cen, id_clk, id_sr;

View File

@ -31,45 +31,37 @@ bool Arch::logicCellsCompatible(const std::vector<const CellInfo *> &cells) cons
int locals_count = 0;
for (auto cell : cells) {
if (bool_or_default(cell->params, id_dff_en)) {
NPNR_ASSERT(cell->belType == TYPE_ICESTORM_LC);
if (cell->lcInfo.dffEnable) {
if (!dffs_exist) {
dffs_exist = true;
cen = get_net_or_empty(cell, id_cen);
clk = get_net_or_empty(cell, id_clk);
sr = get_net_or_empty(cell, id_sr);
cen = cell->lcInfo.cen;
clk = cell->lcInfo.clk;
sr = cell->lcInfo.sr;
if (!isGlobalNet(cen) && cen != nullptr)
if (cen != nullptr && !cen->is_global)
locals_count++;
if (!isGlobalNet(clk) && clk != nullptr)
if (clk != nullptr && !clk->is_global)
locals_count++;
if (!isGlobalNet(sr) && sr != nullptr)
if (sr != nullptr && !sr->is_global)
locals_count++;
if (bool_or_default(cell->params, id_neg_clk)) {
if (cell->lcInfo.negClk) {
dffs_neg = true;
}
} else {
if (cen != get_net_or_empty(cell, id_cen))
if (cen != cell->lcInfo.cen)
return false;
if (clk != get_net_or_empty(cell, id_clk))
if (clk != cell->lcInfo.clk)
return false;
if (sr != get_net_or_empty(cell, id_sr))
if (sr != cell->lcInfo.sr)
return false;
if (dffs_neg != bool_or_default(cell->params, id_neg_clk))
if (dffs_neg != cell->lcInfo.negClk)
return false;
}
}
const NetInfo *i0 = get_net_or_empty(cell, id_i0), *i1 = get_net_or_empty(cell, id_i1),
*i2 = get_net_or_empty(cell, id_i2), *i3 = get_net_or_empty(cell, id_i3);
if (i0 != nullptr)
locals_count++;
if (i1 != nullptr)
locals_count++;
if (i2 != nullptr)
locals_count++;
if (i3 != nullptr)
locals_count++;
locals_count += cell->lcInfo.inputCount;
}
return locals_count <= 32;
@ -116,21 +108,15 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
} else if (cell->type == id_sb_io) {
return getBelPackagePin(bel) != "";
} else if (cell->type == id_sb_gb) {
bool is_reset = false, is_cen = false;
NPNR_ASSERT(cell->ports.at(id_glb_buf_out).net != nullptr);
for (auto user : cell->ports.at(id_glb_buf_out).net->users) {
if (is_reset_port(this, user))
is_reset = true;
if (is_enable_port(this, user))
is_cen = true;
}
const NetInfo *net = cell->ports.at(id_glb_buf_out).net;
IdString glb_net = getWireName(getWireBelPin(bel, PIN_GLOBAL_BUFFER_OUTPUT));
int glb_id = std::stoi(std::string("") + glb_net.str(this).back());
if (is_reset && is_cen)
if (net->is_reset && net->is_enable)
return false;
else if (is_reset)
else if (net->is_reset)
return (glb_id % 2) == 0;
else if (is_cen)
else if (net->is_enable)
return (glb_id % 2) == 1;
else
return true;

View File

@ -54,7 +54,7 @@ enum BelType : int32_t
TYPE_SB_GB,
TYPE_ICESTORM_PLL,
TYPE_SB_WARMBOOT,
TYPE_SB_MAC16,
TYPE_ICESTORM_DSP,
TYPE_ICESTORM_HFOSC,
TYPE_ICESTORM_LFOSC,
TYPE_SB_I2C,
@ -150,6 +150,28 @@ struct DecalId
bool operator!=(const DecalId &other) const { return (type != other.type) || (index != other.index); }
};
struct ArchNetInfo
{
bool is_global = false;
bool is_reset = false, is_enable = false;
};
struct NetInfo;
struct ArchCellInfo
{
BelType belType = TYPE_NONE;
union
{
struct
{
bool dffEnable, negClk;
int inputCount;
const NetInfo *clk, *cen, *sr;
} lcInfo;
};
};
NEXTPNR_NAMESPACE_END
namespace std {
@ -201,5 +223,4 @@ template <> struct hash<NEXTPNR_NAMESPACE_PREFIX DecalId>
return seed;
}
};
} // namespace std

View File

@ -36,7 +36,7 @@ const ConfigEntryPOD &find_config(const TileInfoPOD &tile, const std::string &na
return tile.entries[i];
}
}
NPNR_ASSERT_FALSE("unable to find config bit " + name);
NPNR_ASSERT_FALSE_STR("unable to find config bit " + name);
}
std::tuple<int8_t, int8_t, int8_t> get_ieren(const BitstreamInfoPOD &bi, int8_t x, int8_t y, int8_t z)
@ -90,12 +90,115 @@ std::string get_param_str_or_def(const CellInfo *cell, const IdString param, std
char get_hexdigit(int i) { return std::string("0123456789ABCDEF").at(i); }
static const BelConfigPOD &get_ec_config(const ChipInfoPOD *chip, BelId bel)
{
for (int i = 0; i < chip->num_belcfgs; i++) {
if (chip->bel_config[i].bel_index == bel.index)
return chip->bel_config[i];
}
NPNR_ASSERT_FALSE("failed to find bel config");
}
typedef std::vector<std::vector<std::vector<std::vector<int8_t>>>> chipconfig_t;
static void set_ec_cbit(chipconfig_t &config, const Context *ctx, const BelConfigPOD &cell_cbits, std::string name,
bool value)
{
const ChipInfoPOD *chip = ctx->chip_info;
for (int i = 0; i < cell_cbits.num_entries; i++) {
const auto &cbit = cell_cbits.entries[i];
if (cbit.entry_name.get() == name) {
const auto &ti = chip->bits_info->tiles_nonrouting[tile_at(ctx, cbit.x, cbit.y)];
set_config(ti, config.at(cbit.y).at(cbit.x), std::string("IpConfig.") + cbit.cbit_name.get(), value);
return;
}
}
NPNR_ASSERT_FALSE_STR("failed to config extra cell config bit " + name);
}
void configure_extra_cell(chipconfig_t &config, const Context *ctx, CellInfo *cell,
const std::vector<std::pair<std::string, int>> &params, bool string_style)
{
const ChipInfoPOD *chip = ctx->chip_info;
const auto &bc = get_ec_config(chip, cell->bel);
for (auto p : params) {
std::vector<bool> value;
if (string_style) {
// Lattice's weird string style params, not sure if
// prefixes other than 0b should be supported, only 0b features in docs
std::string raw = get_param_str_or_def(cell, ctx->id(p.first), "0b0");
assert(raw.substr(0, 2) == "0b");
raw = raw.substr(2);
value.resize(raw.length());
for (int i = 0; i < (int)raw.length(); i++) {
if (raw[i] == '1') {
value[(raw.length() - 1) - i] = 1;
} else {
assert(raw[i] == '0');
value[(raw.length() - 1) - i] = 0;
}
}
} else {
int ival = get_param_or_def(cell, ctx->id(p.first), 0);
for (int i = 0; i < p.second; i++)
value.push_back((ival >> i) & 0x1);
}
value.resize(p.second);
if (p.second == 1) {
set_ec_cbit(config, ctx, bc, p.first, value.at(0));
} else {
for (int i = 0; i < p.second; i++) {
set_ec_cbit(config, ctx, bc, p.first + "_" + std::to_string(i), value.at(i));
}
}
}
}
std::string tagTileType(TileType &tile)
{
if (tile == TILE_NONE)
return "";
switch (tile) {
case TILE_LOGIC:
return ".logic_tile";
break;
case TILE_IO:
return ".io_tile";
break;
case TILE_RAMB:
return ".ramb_tile";
break;
case TILE_RAMT:
return ".ramt_tile";
break;
case TILE_DSP0:
return ".dsp0_tile";
break;
case TILE_DSP1:
return ".dsp1_tile";
break;
case TILE_DSP2:
return ".dsp2_tile";
break;
case TILE_DSP3:
return ".dsp3_tile";
break;
case TILE_IPCON:
return ".ipcon_tile";
break;
default:
NPNR_ASSERT(false);
}
}
void write_asc(const Context *ctx, std::ostream &out)
{
// [y][x][row][col]
const ChipInfoPOD &ci = *ctx->chip_info;
const BitstreamInfoPOD &bi = *ci.bits_info;
std::vector<std::vector<std::vector<std::vector<int8_t>>>> config;
chipconfig_t config;
config.resize(ci.height);
for (int y = 0; y < ci.height; y++) {
config.at(y).resize(ci.width);
@ -124,7 +227,7 @@ void write_asc(const Context *ctx, std::ostream &out)
out << ".device 5k" << std::endl;
break;
default:
NPNR_ASSERT_FALSE("unsupported device type");
NPNR_ASSERT_FALSE("unsupported device type\n");
}
// Set pips
for (auto pip : ctx->getPips()) {
@ -192,7 +295,7 @@ void write_asc(const Context *ctx, std::ostream &out)
bool val = (pin_type >> i) & 0x01;
set_config(ti, config.at(y).at(x), "IOB_" + std::to_string(z) + ".PINTYPE_" + std::to_string(i), val);
}
set_config(ti, config.at(y).at(x), "NegClk", neg_trigger);
auto ieren = get_ieren(bi, x, y, z);
int iex, iey, iez;
std::tie(iex, iey, iez) = ieren;
@ -265,6 +368,27 @@ void write_asc(const Context *ctx, std::ostream &out)
NPNR_ASSERT(false);
}
}
} else if (cell.second->type == ctx->id("ICESTORM_DSP")) {
const std::vector<std::pair<std::string, int>> mac16_params = {{"C_REG", 1},
{"A_REG", 1},
{"B_REG", 1},
{"D_REG", 1},
{"TOP_8x8_MULT_REG", 1},
{"BOT_8x8_MULT_REG", 1},
{"PIPELINE_16x16_MULT_REG1", 1},
{"PIPELINE_16x16_MULT_REG2", 1},
{"TOPOUTPUT_SELECT", 2},
{"TOPADDSUB_LOWERINPUT", 2},
{"TOPADDSUB_UPPERINPUT", 1},
{"TOPADDSUB_CARRYSELECT", 2},
{"BOTOUTPUT_SELECT", 2},
{"BOTADDSUB_LOWERINPUT", 2},
{"BOTADDSUB_UPPERINPUT", 1},
{"BOTADDSUB_CARRYSELECT", 2},
{"MODE_8x8", 1},
{"A_SIGNED", 1},
{"B_SIGNED", 1}};
configure_extra_cell(config, ctx, cell.second.get(), mac16_params, false);
} else {
NPNR_ASSERT(false);
}
@ -341,8 +465,9 @@ void write_asc(const Context *ctx, std::ostream &out)
set_config(ti, config.at(y).at(x),
"Cascade.IPCON_LC0" + std::to_string(lc_idx) + "_inmux02_5", true);
else
set_config(ti, config.at(y).at(x), "Cascade.MULT" + std::to_string(int(tile - TILE_DSP0)) +
"_LC0" + std::to_string(lc_idx) + "_inmux02_5",
set_config(ti, config.at(y).at(x),
"Cascade.MULT" + std::to_string(int(tile - TILE_DSP0)) + "_LC0" +
std::to_string(lc_idx) + "_inmux02_5",
true);
}
}
@ -354,39 +479,7 @@ void write_asc(const Context *ctx, std::ostream &out)
for (int y = 0; y < ci.height; y++) {
for (int x = 0; x < ci.width; x++) {
TileType tile = tile_at(ctx, x, y);
if (tile == TILE_NONE)
continue;
switch (tile) {
case TILE_LOGIC:
out << ".logic_tile";
break;
case TILE_IO:
out << ".io_tile";
break;
case TILE_RAMB:
out << ".ramb_tile";
break;
case TILE_RAMT:
out << ".ramt_tile";
break;
case TILE_DSP0:
out << ".dsp0_tile";
break;
case TILE_DSP1:
out << ".dsp1_tile";
break;
case TILE_DSP2:
out << ".dsp2_tile";
break;
case TILE_DSP3:
out << ".dsp3_tile";
break;
case TILE_IPCON:
out << ".ipcon_tile";
break;
default:
NPNR_ASSERT(false);
}
out << tagTileType(tile);
out << " " << x << " " << y << std::endl;
for (auto row : config.at(y).at(x)) {
for (auto col : row) {
@ -437,4 +530,100 @@ void write_asc(const Context *ctx, std::ostream &out)
}
}
void read_config(Context *ctx, std::istream &in, chipconfig_t &config)
{
constexpr size_t line_buf_size = 65536;
char buffer[line_buf_size];
int tile_x = -1, tile_y = -1, line_nr = -1;
while (1) {
in.getline(buffer, line_buf_size);
if (buffer[0] == '.') {
line_nr = -1;
const char *tok = strtok(buffer, " \t\r\n");
if (!strcmp(tok, ".device")) {
std::string config_device = strtok(nullptr, " \t\r\n");
std::string expected;
switch (ctx->args.type) {
case ArchArgs::LP384:
expected = "384";
break;
case ArchArgs::HX1K:
case ArchArgs::LP1K:
expected = "1k";
break;
case ArchArgs::HX8K:
case ArchArgs::LP8K:
expected = "8k";
break;
case ArchArgs::UP5K:
expected = "5k";
break;
default:
log_error("unsupported device type\n");
}
if (expected != config_device)
log_error("device type does not match\n");
} else if (!strcmp(tok, ".io_tile") || !strcmp(tok, ".logic_tile") || !strcmp(tok, ".ramb_tile") ||
!strcmp(tok, ".ramt_tile") || !strcmp(tok, ".ipcon_tile") || !strcmp(tok, ".dsp0_tile") ||
!strcmp(tok, ".dsp1_tile") || !strcmp(tok, ".dsp2_tile") || !strcmp(tok, ".dsp3_tile")) {
line_nr = 0;
tile_x = atoi(strtok(nullptr, " \t\r\n"));
tile_y = atoi(strtok(nullptr, " \t\r\n"));
TileType tile = tile_at(ctx, tile_x, tile_y);
if (tok != tagTileType(tile))
log_error("Wrong tile type for specified position\n");
} else if (!strcmp(tok, ".extra_bit")) {
/*
int b = atoi(strtok(nullptr, " \t\r\n"));
int x = atoi(strtok(nullptr, " \t\r\n"));
int y = atoi(strtok(nullptr, " \t\r\n"));
std::tuple<int, int, int> key(b, x, y);
extra_bits.insert(key);
*/
} else if (!strcmp(tok, ".sym")) {
int net = atoi(strtok(nullptr, " \t\r\n")); (void)net;
const char *name = strtok(nullptr, " \t\r\n");
std::unique_ptr<NetInfo> created_net = std::unique_ptr<NetInfo>(new NetInfo);
created_net->name = ctx->id(name);
ctx->nets[created_net->name] = std::move(created_net);
}
} else if (line_nr >= 0 && strlen(buffer) > 0) {
if (line_nr > int(config.at(tile_y).at(tile_x).size() - 1))
log_error("Invalid data in input asc file");
for (int i = 0; buffer[i] == '0' || buffer[i] == '1'; i++)
config.at(tile_y).at(tile_x).at(line_nr).at(i) = (buffer[i] == '1') ? 1 : 0;
line_nr++;
}
if (in.eof())
break;
}
}
bool read_asc(Context *ctx, std::istream &in)
{
try {
// [y][x][row][col]
const ChipInfoPOD &ci = *ctx->chip_info;
const BitstreamInfoPOD &bi = *ci.bits_info;
chipconfig_t config;
config.resize(ci.height);
for (int y = 0; y < ci.height; y++) {
config.at(y).resize(ci.width);
for (int x = 0; x < ci.width; x++) {
TileType tile = tile_at(ctx, x, y);
int rows = bi.tiles_nonrouting[tile].rows;
int cols = bi.tiles_nonrouting[tile].cols;
config.at(y).at(x).resize(rows, std::vector<int8_t>(cols));
}
}
read_config(ctx, in, config);
return true;
} catch (log_execution_error_exception) {
return false;
}
}
NEXTPNR_NAMESPACE_END

View File

@ -27,6 +27,7 @@
NEXTPNR_NAMESPACE_BEGIN
void write_asc(const Context *ctx, std::ostream &out);
bool read_asc(Context *ctx, std::istream &in);
NEXTPNR_NAMESPACE_END

View File

@ -1,3 +1,3 @@
read_verilog blinky.v
synth_ice40 -top blinky -nocarry
synth_ice40 -top blinky
write_json blinky.json

View File

@ -142,6 +142,71 @@ std::unique_ptr<CellInfo> create_ice_cell(Context *ctx, IdString type, std::stri
for (int i = 0; i < 4; i++) {
add_port(ctx, new_cell.get(), "MASKWREN_" + std::to_string(i), PORT_IN);
}
} else if (type == ctx->id("ICESTORM_DSP")) {
new_cell->params[ctx->id("NEG_TRIGGER")] = "0";
new_cell->params[ctx->id("C_REG")] = "0";
new_cell->params[ctx->id("A_REG")] = "0";
new_cell->params[ctx->id("B_REG")] = "0";
new_cell->params[ctx->id("D_REG")] = "0";
new_cell->params[ctx->id("TOP_8x8_MULT_REG")] = "0";
new_cell->params[ctx->id("BOT_8x8_MULT_REG")] = "0";
new_cell->params[ctx->id("PIPELINE_16x16_MULT_REG1")] = "0";
new_cell->params[ctx->id("PIPELINE_16x16_MULT_REG2")] = "0";
new_cell->params[ctx->id("TOPOUTPUT_SELECT")] = "0";
new_cell->params[ctx->id("TOPADDSUB_LOWERINPUT")] = "0";
new_cell->params[ctx->id("TOPADDSUB_UPPERINPUT")] = "0";
new_cell->params[ctx->id("TOPADDSUB_CARRYSELECT")] = "0";
new_cell->params[ctx->id("BOTOUTPUT_SELECT")] = "0";
new_cell->params[ctx->id("BOTADDSUB_LOWERINPUT")] = "0";
new_cell->params[ctx->id("BOTADDSUB_UPPERINPUT")] = "0";
new_cell->params[ctx->id("BOTADDSUB_CARRYSELECT")] = "0";
new_cell->params[ctx->id("MODE_8x8")] = "0";
new_cell->params[ctx->id("A_SIGNED")] = "0";
new_cell->params[ctx->id("B_SIGNED")] = "0";
add_port(ctx, new_cell.get(), "CLK", PORT_IN);
add_port(ctx, new_cell.get(), "CE", PORT_IN);
for (int i = 0; i < 16; i++) {
add_port(ctx, new_cell.get(), "C_" + std::to_string(i), PORT_IN);
add_port(ctx, new_cell.get(), "A_" + std::to_string(i), PORT_IN);
add_port(ctx, new_cell.get(), "B_" + std::to_string(i), PORT_IN);
add_port(ctx, new_cell.get(), "D_" + std::to_string(i), PORT_IN);
}
add_port(ctx, new_cell.get(), "AHOLD", PORT_IN);
add_port(ctx, new_cell.get(), "BHOLD", PORT_IN);
add_port(ctx, new_cell.get(), "CHOLD", PORT_IN);
add_port(ctx, new_cell.get(), "DHOLD", PORT_IN);
add_port(ctx, new_cell.get(), "IRSTTOP", PORT_IN);
add_port(ctx, new_cell.get(), "IRSTBOT", PORT_IN);
add_port(ctx, new_cell.get(), "ORSTTOP", PORT_IN);
add_port(ctx, new_cell.get(), "ORSTBOT", PORT_IN);
add_port(ctx, new_cell.get(), "OLOADTOP", PORT_IN);
add_port(ctx, new_cell.get(), "OLOADBOT", PORT_IN);
add_port(ctx, new_cell.get(), "ADDSUBTOP", PORT_IN);
add_port(ctx, new_cell.get(), "ADDSUBBOT", PORT_IN);
add_port(ctx, new_cell.get(), "OHOLDTOP", PORT_IN);
add_port(ctx, new_cell.get(), "OHOLDBOT", PORT_IN);
add_port(ctx, new_cell.get(), "CI", PORT_IN);
add_port(ctx, new_cell.get(), "ACCUMCI", PORT_IN);
add_port(ctx, new_cell.get(), "SIGNEXTIN", PORT_IN);
for (int i = 0; i < 32; i++) {
add_port(ctx, new_cell.get(), "O_" + std::to_string(i), PORT_OUT);
}
add_port(ctx, new_cell.get(), "CO", PORT_OUT);
add_port(ctx, new_cell.get(), "ACCUMCO", PORT_OUT);
add_port(ctx, new_cell.get(), "SIGNEXTOUT", PORT_OUT);
} else {
log_error("unable to create iCE40 cell of type %s", type.c_str(ctx));
}
@ -256,7 +321,10 @@ bool is_clock_port(const BaseCtx *ctx, const PortRef &port)
if (port.cell->type == ctx->id("ICESTORM_LC"))
return port.port == ctx->id("CLK");
if (is_ram(ctx, port.cell) || port.cell->type == ctx->id("ICESTORM_RAM"))
return port.port == ctx->id("RCLK") || port.port == ctx->id("WCLK");
return port.port == ctx->id("RCLK") || port.port == ctx->id("WCLK") || port.port == ctx->id("RCLKN") ||
port.port == ctx->id("WCLKN");
if (is_sb_mac16(ctx, port.cell) || port.cell->type == ctx->id("ICESTORM_DSP"))
return port.port == ctx->id("CLK");
return false;
}
@ -268,6 +336,9 @@ bool is_reset_port(const BaseCtx *ctx, const PortRef &port)
return port.port == ctx->id("R") || port.port == ctx->id("S");
if (port.cell->type == ctx->id("ICESTORM_LC"))
return port.port == ctx->id("SR");
if (is_sb_mac16(ctx, port.cell) || port.cell->type == ctx->id("ICESTORM_DSP"))
return port.port == ctx->id("IRSTTOP") || port.port == ctx->id("IRSTBOT") || port.port == ctx->id("ORSTTOP") ||
port.port == ctx->id("ORSTBOT");
return false;
}
@ -279,6 +350,9 @@ bool is_enable_port(const BaseCtx *ctx, const PortRef &port)
return port.port == ctx->id("E");
if (port.cell->type == ctx->id("ICESTORM_LC"))
return port.port == ctx->id("CEN");
// FIXME
// if (is_sb_mac16(ctx, port.cell) || port.cell->type == ctx->id("ICESTORM_DSP"))
// return port.port == ctx->id("CE");
return false;
}

View File

@ -69,6 +69,8 @@ inline bool is_sb_hfosc(const BaseCtx *ctx, const CellInfo *cell) { return cell-
inline bool is_sb_spram(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_SPRAM256KA"); }
inline bool is_sb_mac16(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_MAC16"); }
// Convert a SB_LUT primitive to (part of) an ICESTORM_LC, swapping ports
// as needed. Set no_dff if a DFF is not being used, so that the output
// can be reconnected

View File

@ -38,7 +38,7 @@ switches = list()
ierens = list()
extra_cells = dict()
extra_cell_config = dict()
packages = list()
wire_uphill_belport = dict()
@ -159,7 +159,7 @@ def wire_type(name):
name = name.split('/')[-1]
wt = None
if name.startswith("glb_netwk_"):
if name.startswith("glb_netwk_") or name.startswith("padin_"):
wt = "GLOBAL"
elif name.startswith("D_IN_") or name.startswith("D_OUT_"):
wt = "LOCAL"
@ -432,6 +432,19 @@ with open(args.filename, "r") as f:
extra_cells[mode[1]].append((line[0], (int(line[1]), int(line[2]), line[3])))
continue
def add_wire(x, y, name):
global num_wires
wire_idx = num_wires
num_wires = num_wires + 1
wname = (x, y, name)
wire_names[wname] = wire_idx
wire_names_r[wire_idx] = wname
wire_segments[wire_idx] = dict()
# Add virtual padin wires
for i in range(8):
add_wire(0, 0, "padin_%d" % i)
def add_bel_input(bel, wire, port):
if wire not in wire_downhill_belports:
wire_downhill_belports[wire] = set()
@ -567,6 +580,7 @@ def is_ec_output(ec_entry):
def add_bel_ec(ec):
ectype, x, y, z = ec
bel = len(bel_name)
extra_cell_config[bel] = []
bel_name.append("X%d/Y%d/%s_%d" % (x, y, ectype.lower(), z))
bel_type.append(ectype)
bel_pos.append((x, y, z))
@ -578,8 +592,7 @@ def add_bel_ec(ec):
else:
add_bel_input(bel, wire_names[entry[1]], entry[0])
else:
# Configuration bit, need to create a structure for these
pass
extra_cell_config[bel].append(entry)
for tile_xy, tile_type in sorted(tiles.items()):
if tile_type == "logic":
@ -1175,6 +1188,23 @@ bba.l("tile_grid_%s" % dev_name, "TileType")
for t in tilegrid:
bba.u32(tiletypes[t], "tiletype")
for bel_idx, entries in sorted(extra_cell_config.items()):
if len(entries) > 0:
bba.l("bel%d_config_entries" % bel_idx, "BelConfigEntryPOD")
for entry in entries:
bba.s(entry[0], "entry_name")
bba.s(entry[1][2], "cbit_name")
bba.u8(entry[1][0], "x")
bba.u8(entry[1][1], "y")
bba.u16(0, "padding")
if len(extra_cell_config) > 0:
bba.l("bel_config_%s" % dev_name, "BelConfigPOD")
for bel_idx, entries in sorted(extra_cell_config.items()):
bba.u32(bel_idx, "bel_index")
bba.u32(len(entries), "num_entries")
bba.r("bel%d_config_entries" % bel_idx if len(entries) > 0 else None, "entries")
bba.l("package_info_%s" % dev_name, "PackageInfoPOD")
for info in packageinfo:
bba.s(info[0], "name")
@ -1188,12 +1218,14 @@ bba.u32(len(bel_name), "num_bels")
bba.u32(num_wires, "num_wires")
bba.u32(len(pipinfo), "num_pips")
bba.u32(len(switchinfo), "num_switches")
bba.u32(len(extra_cell_config), "num_belcfgs")
bba.u32(len(packageinfo), "num_packages")
bba.r("bel_data_%s" % dev_name, "bel_data")
bba.r("wire_data_%s" % dev_name, "wire_data")
bba.r("pip_data_%s" % dev_name, "pip_data")
bba.r("tile_grid_%s" % dev_name, "tile_grid")
bba.r("bits_info_%s" % dev_name, "bits_info")
bba.r("bel_config_%s" % dev_name if len(extra_cell_config) > 0 else None, "bel_config")
bba.r("package_info_%s" % dev_name, "packages_data")
bba.finalize()

View File

@ -640,10 +640,8 @@ static bool getWireXY_local(GfxTileWireId id, float &x, float &y)
return false;
}
void pipGfx(std::vector<GraphicElement> &g, int x, int y,
float x1, float y1, float x2, float y2,
float swx1, float swy1, float swx2, float swy2,
GraphicElement::style_t style)
void pipGfx(std::vector<GraphicElement> &g, int x, int y, float x1, float y1, float x2, float y2, float swx1,
float swy1, float swx2, float swy2, GraphicElement::style_t style)
{
float tx = 0.5 * (x1 + x2);
float ty = 0.5 * (y1 + y2);
@ -693,7 +691,8 @@ edge_pip:
g.push_back(el);
}
void gfxTilePip(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId src, GfxTileWireId dst, GraphicElement::style_t style)
void gfxTilePip(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId src, GfxTileWireId dst,
GraphicElement::style_t style)
{
float x1, y1, x2, y2;

View File

@ -468,7 +468,8 @@ enum GfxTileWireId
};
void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId id, GraphicElement::style_t style);
void gfxTilePip(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId src, GfxTileWireId dst, GraphicElement::style_t style);
void gfxTilePip(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId src, GfxTileWireId dst,
GraphicElement::style_t style);
NEXTPNR_NAMESPACE_END

View File

@ -66,6 +66,12 @@ void svg_dump_decal(const Context *ctx, const DecalXY &decal)
}
}
void conflicting_options(const boost::program_options::variables_map &vm, const char *opt1, const char *opt2)
{
if (vm.count(opt1) && !vm[opt1].defaulted() && vm.count(opt2) && !vm[opt2].defaulted())
log_error((std::string("Conflicting options '") + opt1 + "' and '" + opt2 + "'.").c_str());
}
int main(int argc, char *argv[])
{
try {
@ -95,6 +101,7 @@ int main(int argc, char *argv[])
options.add_options()("json", po::value<std::string>(), "JSON design file to ingest");
options.add_options()("pcf", po::value<std::string>(), "PCF constraints file to ingest");
options.add_options()("asc", po::value<std::string>(), "asc bitstream file to write");
options.add_options()("read", po::value<std::string>(), "asc bitstream file to read");
options.add_options()("seed", po::value<int>(), "seed value for random number generator");
options.add_options()("version,V", "show version");
options.add_options()("tmfuzz", "run path delay estimate fuzzer");
@ -121,13 +128,17 @@ int main(int argc, char *argv[])
po::store(parsed, vm);
po::notify(vm);
}
catch (std::exception &e) {
} catch (std::exception &e) {
std::cout << e.what() << "\n";
return 1;
}
conflicting_options(vm, "read", "json");
#ifndef ICE40_HX1K_ONLY
if ((vm.count("lp384") + vm.count("lp1k") + vm.count("lp8k") + vm.count("hx1k") + vm.count("hx8k") +
vm.count("up5k")) > 1)
log_error("Only one device type can be set\n");
#endif
if (vm.count("help") || argc == 1) {
help:
std::cout << boost::filesystem::basename(argv[0])
@ -353,6 +364,13 @@ int main(int argc, char *argv[])
if (vm.count("no-tmdriv"))
ctx->timing_driven = false;
if (vm.count("read")) {
std::string filename = vm["read"].as<std::string>();
std::ifstream f(filename);
if (!read_asc(ctx.get(), f))
log_error("Loading ASC failed.\n");
}
#ifndef NO_GUI
if (vm.count("gui")) {
Application a(argc, argv);

View File

@ -277,6 +277,10 @@ static void pack_ram(Context *ctx)
if (bpos != std::string::npos) {
newname = newname.substr(0, bpos) + "_" + newname.substr(bpos + 1, (newname.size() - bpos) - 2);
}
if (pi.name == ctx->id("RCLKN"))
newname = "RCLK";
else if (pi.name == ctx->id("WCLKN"))
newname = "WCLK";
replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
}
new_cells.push_back(std::move(packed));
@ -303,6 +307,10 @@ static void set_net_constant(const Context *ctx, NetInfo *orig, NetInfo *constne
if ((is_lut(ctx, uc) || is_lc(ctx, uc) || is_carry(ctx, uc)) && (user.port.str(ctx).at(0) == 'I') &&
!constval) {
uc->ports[user.port].net = nullptr;
} else if ((is_sb_mac16(ctx, uc) || uc->type == ctx->id("ICESTORM_DSP")) &&
(user.port != ctx->id("CLK") &&
((constval && user.port == ctx->id("CE")) || (!constval && user.port != ctx->id("CE"))))) {
uc->ports[user.port].net = nullptr;
} else {
uc->ports[user.port].net = constnet;
constnet->users.push_back(user);
@ -564,6 +572,25 @@ static void pack_special(Context *ctx)
replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
}
new_cells.push_back(std::move(packed));
} else if (is_sb_mac16(ctx, ci)) {
std::unique_ptr<CellInfo> packed =
create_ice_cell(ctx, ctx->id("ICESTORM_DSP"), ci->name.str(ctx) + "_DSP");
packed_cells.insert(ci->name);
for (auto attr : ci->attrs)
packed->attrs[attr.first] = attr.second;
for (auto param : ci->params)
packed->params[param.first] = param.second;
for (auto port : ci->ports) {
PortInfo &pi = port.second;
std::string newname = pi.name.str(ctx);
size_t bpos = newname.find('[');
if (bpos != std::string::npos) {
newname = newname.substr(0, bpos) + "_" + newname.substr(bpos + 1, (newname.size() - bpos) - 2);
}
replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
}
new_cells.push_back(std::move(packed));
}
}
@ -589,6 +616,7 @@ bool Arch::pack()
pack_carries(ctx);
pack_ram(ctx);
pack_special(ctx);
ctx->assignArchInfo();
log_info("Checksum: 0x%08x\n", ctx->checksum());
return true;
} catch (log_execution_error_exception) {

View File

@ -2,5 +2,5 @@
set -ex
rm -f picorv32.v
wget https://raw.githubusercontent.com/cliffordwolf/picorv32/master/picorv32.v
yosys -p 'synth_ice40 -nocarry -json picorv32.json -top top' picorv32.v picorv32_top.v
yosys -p 'synth_ice40 -json picorv32.json -top top' picorv32.v picorv32_top.v
../nextpnr-ice40 --hx8k --asc picorv32.asc --json picorv32.json

View File

@ -1,9 +0,0 @@
#!/bin/bash
set -ex
rm -f picorv32.v
wget https://raw.githubusercontent.com/cliffordwolf/picorv32/master/picorv32.v
yosys -p 'synth_ice40 -nocarry -blif picorv32.blif -top top' picorv32.v picorv32_top.v
arachne-pnr -d 8k --post-place-blif picorv32_place.blif picorv32.blif -o picorv32_arachne_all.asc
yosys -p "read_blif -wideports picorv32_place.blif; read_verilog -lib +/ice40/cells_sim.v; write_json picorv32_place.json"
./transform_arachne_loc.py picorv32_place.json > picorv32_place_nx.json
../nextpnr-ice40 --hx8k --asc picorv32_ar_placed.asc --json picorv32_place_nx.json --force

View File

@ -127,6 +127,7 @@ class PlacementLegaliser
legalise_others();
legalise_logic_tiles();
bool replaced_cells = replace_cells();
ctx->assignArchInfo();
return legalised_carries && replaced_cells;
}
@ -371,6 +372,7 @@ class PlacementLegaliser
NPNR_ASSERT(ctx->nets.find(co_i3_name) == ctx->nets.end());
ctx->nets[co_i3_name] = std::move(co_i3_net);
IdString name = lc->name;
ctx->assignCellInfo(lc.get());
ctx->cells[lc->name] = std::move(lc);
createdCells.insert(name);
return ctx->cells[name].get();
@ -415,6 +417,7 @@ class PlacementLegaliser
ctx->nets[out_net_name] = std::move(out_net);
IdString name = lc->name;
ctx->assignCellInfo(lc.get());
ctx->cells[lc->name] = std::move(lc);
createdCells.insert(name);
return ctx->cells[name].get();

View File

@ -1,24 +0,0 @@
#!/usr/bin/env python3
import json
import sys
import re
with open(sys.argv[1]) as f:
data = json.load(f)
for mod, moddata in data["modules"].items():
if "cells" in moddata:
for cell, celldata in moddata["cells"].items():
pos = re.split('[,/]', celldata["attributes"]["loc"])
pos = [int(_) for _ in pos]
if celldata["type"] == "ICESTORM_LC":
celldata["attributes"]["BEL"] = "X%d/Y%d/lc%d" % (pos[0], pos[1], pos[2])
elif celldata["type"] == "SB_IO":
celldata["attributes"]["BEL"] = "X%d/Y%d/io%d" % (pos[0], pos[1], pos[2])
elif "RAM" in celldata["type"]:
celldata["attributes"]["BEL"] = "X%d/Y%d/ram" % (pos[0], pos[1])
elif celldata["type"] == "SB_GB":
celldata["attributes"]["BEL"] = "X%d/Y%d/gb" % (pos[0], pos[1])
else:
assert False
print(json.dumps(data, sort_keys=True, indent=4))