mirror of https://github.com/YosysHQ/nextpnr.git
701 lines
24 KiB
C++
701 lines
24 KiB
C++
#include "design_utils.h"
|
||
#include "log.h"
|
||
#include "nextpnr.h"
|
||
|
||
#define HIMBAECHEL_CONSTIDS "uarch/gowin/constids.inc"
|
||
#include "himbaechel_constids.h"
|
||
#include "himbaechel_helpers.h"
|
||
|
||
#include "gowin.h"
|
||
#include "gowin_utils.h"
|
||
#include "pack.h"
|
||
|
||
#include <cinttypes>
|
||
|
||
NEXTPNR_NAMESPACE_BEGIN
|
||
|
||
// ===================================
|
||
// Block RAM
|
||
// ===================================
|
||
// Memory is always 36-bit, consisting of 9-bit bytes; 8-bit primitives are
|
||
// formed by “skipping” every 9th bit on the DI and DO buses. Here, the
|
||
// skipping works as follows: if the primitive is 8-bit (16 or 32), then every
|
||
// 9th bit becomes the 10th bit and so on.
|
||
void GowinPacker::bsram_rename_ports(CellInfo *ci, int bit_width, char const *from, char const *to, int offset)
|
||
{
|
||
int num = (bit_width == 9 || bit_width == 18 || bit_width == 36) ? 36 : 32;
|
||
for (int i = 0, j = offset; i < num; ++i, ++j) {
|
||
if (i && (i % 8) == 0 && (bit_width == 16 || bit_width == 32)) {
|
||
++j;
|
||
}
|
||
ci->renamePort(ctx->idf(from, i), ctx->idf(to, offset ? j % 36 : j));
|
||
}
|
||
}
|
||
|
||
// We solve the BLKSEL problems that are observed on some chips by
|
||
// connecting the BLKSEL ports to constant networks so that this BSRAM will
|
||
// be selected, the actual selection is made by manipulating the Clock
|
||
// Enable pin using a LUT-based decoder.
|
||
void GowinPacker::bsram_fix_blksel(CellInfo *ci, std::vector<std::unique_ptr<CellInfo>> &new_cells)
|
||
{
|
||
// is BSRAM enabled
|
||
NetInfo *ce_net = ci->getPort(id_CE);
|
||
if (ce_net == nullptr || ce_net->name == ctx->id("$PACKER_GND")) {
|
||
return;
|
||
}
|
||
|
||
// port name, BLK_SEL parameter for this port
|
||
std::vector<std::pair<IdString, int>> dyn_blksel;
|
||
|
||
int blk_sel_parameter = ci->params.at(id_BLK_SEL).as_int64();
|
||
for (int i = 0; i < 3; ++i) {
|
||
IdString pin_name = ctx->idf("BLKSEL[%d]", i);
|
||
NetInfo *net = ci->getPort(pin_name);
|
||
if (net == nullptr || net->name == ctx->id("$PACKER_GND") || net->name == ctx->id("$PACKER_VCC")) {
|
||
continue;
|
||
}
|
||
dyn_blksel.push_back(std::make_pair(pin_name, (blk_sel_parameter >> i) & 1));
|
||
}
|
||
|
||
if (dyn_blksel.empty()) {
|
||
return;
|
||
}
|
||
|
||
if (ctx->verbose) {
|
||
log_info(" apply the BSRAM BLKSEL fix\n");
|
||
}
|
||
|
||
// Make a decoder
|
||
auto lut_cell = gwu.create_cell(gwu.create_aux_name(ci->name, 0, "_blksel_lut$"), id_LUT4);
|
||
CellInfo *lut = lut_cell.get();
|
||
lut->addInput(id_I3);
|
||
ci->movePortTo(id_CE, lut, id_I3);
|
||
lut->addOutput(id_F);
|
||
ci->connectPorts(id_CE, lut, id_F);
|
||
|
||
NetInfo *vcc_net = ctx->nets.at(ctx->id("$PACKER_VCC")).get();
|
||
NetInfo *vss_net = ctx->nets.at(ctx->id("$PACKER_GND")).get();
|
||
|
||
// Connected CE to I3 to make it easy to calculate the decoder
|
||
int init = 0x100; // CE == 0 --> F = 0
|
||
// CE == 1 --> F = decoder result
|
||
int idx = 0;
|
||
for (auto &port : dyn_blksel) {
|
||
IdString lut_input_name = ctx->idf("I%d", idx);
|
||
ci->movePortTo(port.first, lut, lut_input_name);
|
||
if (port.second) {
|
||
init <<= (1 << idx);
|
||
ci->connectPort(port.first, vcc_net);
|
||
} else {
|
||
ci->connectPort(port.first, vss_net);
|
||
}
|
||
++idx;
|
||
}
|
||
lut->setParam(id_INIT, init);
|
||
|
||
new_cells.push_back(std::move(lut_cell));
|
||
}
|
||
|
||
// Some chips cannot, for some reason, use internal BSRAM registers to
|
||
// implement READ_MODE=1'b1 (pipeline) with a word width other than 32 or
|
||
// 36 bits.
|
||
// We work around this by adding an external DFF and using BSRAM
|
||
// as READ_MODE=1'b0 (bypass).
|
||
void GowinPacker::bsram_fix_outreg(CellInfo *ci, int bit_width, IdString ce_pin, IdString oce_pin, IdString clk_pin,
|
||
IdString reset_pin, IdString do_pin, IdString read_mode_param,
|
||
std::vector<std::unique_ptr<CellInfo>> &new_cells)
|
||
{
|
||
if (ci->params.at(read_mode_param).as_int64() == 0) {
|
||
return;
|
||
}
|
||
NetInfo *ce_net = ci->getPort(ce_pin);
|
||
NetInfo *oce_net = ci->getPort(oce_pin);
|
||
if (ce_net == nullptr || oce_net == nullptr) {
|
||
return;
|
||
}
|
||
if (ce_net->name == ctx->id("$PACKER_GND") || oce_net->name == ctx->id("$PACKER_GND")) {
|
||
return;
|
||
}
|
||
|
||
if (ctx->verbose) {
|
||
log_info(" apply the BSRAM OUTREG fix\n");
|
||
}
|
||
ci->setParam(read_mode_param, 0);
|
||
ci->disconnectPort(oce_pin);
|
||
ci->connectPort(oce_pin, ce_net);
|
||
|
||
NetInfo *reset_net = ci->getPort(reset_pin);
|
||
bool sync_reset = ci->params.at(id_RESET_MODE).as_string() == std::string("SYNC");
|
||
IdString dff_type = sync_reset ? id_DFFRE : id_DFFCE;
|
||
IdString reset_port = sync_reset ? id_RESET : id_CLEAR;
|
||
|
||
for (int i = 0; i < bit_width; ++i) {
|
||
IdString do_name = ctx->idf("%s[%d]", do_pin.c_str(ctx), i);
|
||
const NetInfo *net = ci->getPort(do_name);
|
||
if (net != nullptr) {
|
||
if (net->users.empty()) {
|
||
ci->disconnectPort(do_name);
|
||
continue;
|
||
}
|
||
|
||
// create DFF
|
||
auto cache_dff_cell = gwu.create_cell(gwu.create_aux_name(ci->name, i, "_cache_dff$"), dff_type);
|
||
CellInfo *cache_dff = cache_dff_cell.get();
|
||
cache_dff->addInput(id_CE);
|
||
cache_dff->connectPort(id_CE, oce_net);
|
||
|
||
cache_dff->addInput(reset_port);
|
||
cache_dff->connectPort(reset_port, reset_net);
|
||
|
||
ci->copyPortTo(clk_pin, cache_dff, id_CLK);
|
||
|
||
cache_dff->addOutput(id_Q);
|
||
ci->movePortTo(do_name, cache_dff, id_Q);
|
||
|
||
cache_dff->addInput(id_D);
|
||
ci->connectPorts(do_name, cache_dff, id_D);
|
||
|
||
new_cells.push_back(std::move(cache_dff_cell));
|
||
}
|
||
}
|
||
}
|
||
|
||
// Analysis of the images generated by the IDE showed that some components
|
||
// are being added at the input and output of the BSRAM. Two LUTs are
|
||
// added on the WRE and CE inputs (strangely, OCE is not affected), a pair
|
||
// of LUT-DFFs on each DO output, and one or two flipflops of different
|
||
// types in the auxiliary network.
|
||
// The semantics of these additions are unclear, but we can replicate this behavior.
|
||
// Fix BSRAM in single port mode.
|
||
void GowinPacker::bsram_fix_sp(CellInfo *ci, std::vector<std::unique_ptr<CellInfo>> &new_cells)
|
||
{
|
||
int bit_width = ci->params.at(id_BIT_WIDTH).as_int64();
|
||
|
||
if (ctx->verbose) {
|
||
log_info(" apply the SP fix\n");
|
||
}
|
||
// create WRE LUT
|
||
auto wre_lut_cell = gwu.create_cell(gwu.create_aux_name(ci->name, 0, "_wre_lut$"), id_LUT4);
|
||
CellInfo *wre_lut = wre_lut_cell.get();
|
||
wre_lut->setParam(id_INIT, 0x8888);
|
||
ci->movePortTo(id_CE, wre_lut, id_I0);
|
||
ci->movePortTo(id_WRE, wre_lut, id_I1);
|
||
wre_lut->addOutput(id_F);
|
||
ci->connectPorts(id_WRE, wre_lut, id_F);
|
||
|
||
// create CE LUT
|
||
auto ce_lut_cell = gwu.create_cell(gwu.create_aux_name(ci->name, 0, "_ce_lut$"), id_LUT4);
|
||
CellInfo *ce_lut = ce_lut_cell.get();
|
||
ce_lut->setParam(id_INIT, 0xeeee);
|
||
wre_lut->copyPortTo(id_I0, ce_lut, id_I0);
|
||
wre_lut->copyPortTo(id_I1, ce_lut, id_I1);
|
||
ce_lut->addOutput(id_F);
|
||
ci->connectPorts(id_CE, ce_lut, id_F);
|
||
|
||
// create ce reg
|
||
int write_mode = ci->params.at(id_WRITE_MODE).as_int64();
|
||
IdString dff_type = write_mode ? id_DFF : id_DFFR;
|
||
auto ce_pre_dff_cell = gwu.create_cell(gwu.create_aux_name(ci->name, 0, "_ce_pre_dff$"), dff_type);
|
||
CellInfo *ce_pre_dff = ce_pre_dff_cell.get();
|
||
ce_pre_dff->addInput(id_D);
|
||
ce_lut->copyPortTo(id_I0, ce_pre_dff, id_D);
|
||
ci->copyPortTo(id_CLK, ce_pre_dff, id_CLK);
|
||
if (dff_type == id_DFFR) {
|
||
wre_lut->copyPortTo(id_I1, ce_pre_dff, id_RESET);
|
||
}
|
||
ce_pre_dff->addOutput(id_Q);
|
||
|
||
// new ce src with Q pin (used by output pins, not by BSRAM itself)
|
||
CellInfo *new_ce_net_src = ce_pre_dff;
|
||
|
||
// add delay register in pipeline mode
|
||
int read_mode = ci->params.at(id_READ_MODE).as_int64();
|
||
if (read_mode) {
|
||
auto ce_pipe_dff_cell = gwu.create_cell(gwu.create_aux_name(ci->name, 0, "_ce_pipe_dff$"), id_DFF);
|
||
new_cells.push_back(std::move(ce_pipe_dff_cell));
|
||
CellInfo *ce_pipe_dff = new_cells.back().get();
|
||
ce_pipe_dff->addInput(id_D);
|
||
new_ce_net_src->connectPorts(id_Q, ce_pipe_dff, id_D);
|
||
ci->copyPortTo(id_CLK, ce_pipe_dff, id_CLK);
|
||
ce_pipe_dff->addOutput(id_Q);
|
||
new_ce_net_src = ce_pipe_dff;
|
||
}
|
||
|
||
// used outputs of the BSRAM convert to cached
|
||
for (int i = 0; i < bit_width; ++i) {
|
||
IdString do_name = ctx->idf("DO[%d]", i);
|
||
const NetInfo *net = ci->getPort(do_name);
|
||
if (net != nullptr) {
|
||
if (net->users.empty()) {
|
||
ci->disconnectPort(do_name);
|
||
continue;
|
||
}
|
||
// create cache lut
|
||
auto cache_lut_cell = gwu.create_cell(gwu.create_aux_name(ci->name, i, "_cache_lut$"), id_LUT4);
|
||
CellInfo *cache_lut = cache_lut_cell.get();
|
||
cache_lut->setParam(id_INIT, 0xcaca);
|
||
cache_lut->addInput(id_I0);
|
||
cache_lut->addInput(id_I1);
|
||
cache_lut->addInput(id_I2);
|
||
ci->movePortTo(do_name, cache_lut, id_F);
|
||
ci->connectPorts(do_name, cache_lut, id_I1);
|
||
new_ce_net_src->connectPorts(id_Q, cache_lut, id_I2);
|
||
|
||
// create cache DFF
|
||
auto cache_dff_cell = gwu.create_cell(gwu.create_aux_name(ci->name, i, "_cache_dff$"), id_DFFE);
|
||
CellInfo *cache_dff = cache_dff_cell.get();
|
||
cache_dff->addInput(id_CE);
|
||
cache_dff->addInput(id_D);
|
||
ci->copyPortTo(id_CLK, cache_dff, id_CLK);
|
||
new_ce_net_src->connectPorts(id_Q, cache_dff, id_CE);
|
||
cache_lut->copyPortTo(id_I1, cache_dff, id_D);
|
||
cache_dff->addOutput(id_Q);
|
||
cache_dff->connectPorts(id_Q, cache_lut, id_I0);
|
||
|
||
new_cells.push_back(std::move(cache_lut_cell));
|
||
new_cells.push_back(std::move(cache_dff_cell));
|
||
}
|
||
}
|
||
|
||
new_cells.push_back(std::move(wre_lut_cell));
|
||
new_cells.push_back(std::move(ce_lut_cell));
|
||
new_cells.push_back(std::move(ce_pre_dff_cell));
|
||
}
|
||
|
||
void GowinPacker::pack_ROM(CellInfo *ci)
|
||
{
|
||
int default_bw = 32;
|
||
// XXX use block 111
|
||
ci->setParam(ctx->id("BLK_SEL"), Property(7, 32));
|
||
if (ci->type == id_pROM) {
|
||
ci->setAttr(id_BSRAM_SUBTYPE, Property(""));
|
||
} else {
|
||
ci->setAttr(id_BSRAM_SUBTYPE, Property("X9"));
|
||
default_bw = 36;
|
||
}
|
||
|
||
NetInfo *vcc_net = ctx->nets.at(ctx->id("$PACKER_VCC")).get();
|
||
NetInfo *vss_net = ctx->nets.at(ctx->id("$PACKER_GND")).get();
|
||
for (int i = 0; i < 3; ++i) {
|
||
IdString port = ctx->idf("BLKSEL%d", i);
|
||
ci->addInput(port);
|
||
ci->connectPort(port, vcc_net);
|
||
port = ctx->idf("BLKSELB%d", i);
|
||
ci->addInput(port);
|
||
ci->connectPort(port, vcc_net);
|
||
}
|
||
|
||
ci->addInput(id_WRE);
|
||
ci->connectPort(id_WRE, vss_net);
|
||
ci->addInput(id_WREB);
|
||
ci->connectPort(id_WREB, vss_net);
|
||
|
||
if (!ci->params.count(id_BIT_WIDTH)) {
|
||
ci->setParam(id_BIT_WIDTH, Property(default_bw, 32));
|
||
}
|
||
|
||
int bit_width = ci->params.at(id_BIT_WIDTH).as_int64();
|
||
if (bit_width == 32 || bit_width == 36) {
|
||
ci->copyPortTo(id_CLK, ci, id_CLKB);
|
||
ci->copyPortTo(id_CE, ci, id_CEB);
|
||
ci->copyPortTo(id_OCE, ci, id_OCEB);
|
||
ci->copyPortTo(id_RESET, ci, id_RESETB);
|
||
|
||
for (int i = 0; i < 14; ++i) {
|
||
ci->renamePort(ctx->idf("AD[%d]", i), ctx->idf("ADA%d", i));
|
||
ci->copyPortTo(ctx->idf("ADA%d", i), ci, ctx->idf("ADB%d", i));
|
||
}
|
||
bsram_rename_ports(ci, bit_width, "DO[%d]", "DO%d");
|
||
} else {
|
||
// use port B
|
||
ci->renamePort(id_CLK, id_CLKB);
|
||
ci->renamePort(id_OCE, id_OCEB);
|
||
ci->renamePort(id_CE, id_CEB);
|
||
ci->renamePort(id_RESET, id_RESETB);
|
||
|
||
ci->addInput(id_CEA);
|
||
ci->connectPort(id_CEA, vss_net);
|
||
for (int i = 0; i < 14; ++i) {
|
||
ci->renamePort(ctx->idf("AD[%d]", i), ctx->idf("ADB%d", i));
|
||
}
|
||
bsram_rename_ports(ci, bit_width, "DO[%d]", "DO%d", 18);
|
||
}
|
||
}
|
||
|
||
void GowinPacker::divide_sdp(CellInfo *ci, std::vector<std::unique_ptr<CellInfo>> &new_cells)
|
||
{
|
||
if (ctx->verbose) {
|
||
log_info(" divide SDP\n");
|
||
}
|
||
|
||
int bw = ci->params.at(id_BIT_WIDTH_0).as_int64();
|
||
NetInfo *vcc_net = ctx->nets.at(ctx->id("$PACKER_VCC")).get();
|
||
NetInfo *vss_net = ctx->nets.at(ctx->id("$PACKER_GND")).get();
|
||
|
||
IdString cell_type = bw == 32 ? id_SDPB : id_SDPX9B;
|
||
IdString name = ctx->idf("%s_AUX", ctx->nameOf(ci));
|
||
|
||
auto sdp_cell = gwu.create_cell(name, cell_type);
|
||
CellInfo *sdp = sdp_cell.get();
|
||
sdp->setAttr(id_AUX, 1);
|
||
|
||
int new_bw = bw / 2;
|
||
ci->setParam(id_BIT_WIDTH_0, new_bw);
|
||
ci->setParam(id_BIT_WIDTH_1, new_bw);
|
||
sdp->params = ci->params;
|
||
sdp->setParam(id_BIT_WIDTH_0, new_bw);
|
||
sdp->setParam(id_BIT_WIDTH_1, new_bw);
|
||
|
||
// copy control ports
|
||
ci->copyPortBusTo(ctx->id("BLKSELA"), 0, true, sdp, ctx->id("BLKSELA"), 0, true, 3);
|
||
ci->copyPortBusTo(ctx->id("BLKSELB"), 0, true, sdp, ctx->id("BLKSELB"), 0, true, 3);
|
||
ci->copyPortTo(id_CEA, sdp, id_CEA);
|
||
ci->copyPortTo(id_CEB, sdp, id_CEB);
|
||
ci->copyPortTo(id_CLKA, sdp, id_CLKA);
|
||
ci->copyPortTo(id_CLKB, sdp, id_CLKB);
|
||
ci->copyPortTo(id_OCE, sdp, id_OCE);
|
||
ci->copyPortTo(id_RESET, sdp, id_RESET);
|
||
|
||
// Separate port A
|
||
ci->movePortTo(ctx->id("ADA[2]"), sdp, ctx->id("ADA[0]"));
|
||
ci->movePortTo(ctx->id("ADA[3]"), sdp, ctx->id("ADA[1]"));
|
||
|
||
ci->addInput(ctx->id("ADA[2]"));
|
||
ci->addInput(ctx->id("ADA[3]"));
|
||
ci->connectPort(ctx->id("ADA[2]"), vss_net);
|
||
ci->connectPort(ctx->id("ADA[3]"), vss_net);
|
||
|
||
sdp->addInput(ctx->id("ADA[2]"));
|
||
sdp->addInput(ctx->id("ADA[3]"));
|
||
sdp->connectPort(ctx->id("ADA[2]"), vss_net);
|
||
sdp->connectPort(ctx->id("ADA[3]"), vss_net);
|
||
|
||
ci->disconnectPort(ctx->id("ADA[4]"));
|
||
ci->connectPort(ctx->id("ADA[4]"), vss_net);
|
||
sdp->addInput(ctx->id("ADA[4]"));
|
||
sdp->connectPort(ctx->id("ADA[4]"), vcc_net);
|
||
|
||
ci->copyPortBusTo(id_ADA, 5, true, sdp, id_ADA, 5, true, 9);
|
||
|
||
// Separate port B
|
||
for (int i = 0; i < 4; ++i) {
|
||
IdString port = ctx->idf("ADB[%d]", i);
|
||
ci->disconnectPort(port);
|
||
ci->connectPort(port, vss_net);
|
||
ci->copyPortTo(port, sdp, port);
|
||
}
|
||
|
||
ci->disconnectPort(ctx->id("ADB[4]"));
|
||
ci->connectPort(ctx->id("ADB[4]"), vss_net);
|
||
sdp->addInput(ctx->id("ADB[4]"));
|
||
sdp->connectPort(ctx->id("ADB[4]"), vcc_net);
|
||
|
||
ci->copyPortBusTo(id_ADB, 5, true, sdp, id_ADB, 5, true, 9);
|
||
|
||
ci->movePortBusTo(id_DI, new_bw, true, sdp, id_DI, 0, true, new_bw);
|
||
ci->movePortBusTo(id_DO, new_bw, true, sdp, id_DO, 0, true, new_bw);
|
||
|
||
new_cells.push_back(std::move(sdp_cell));
|
||
}
|
||
|
||
void GowinPacker::pack_SDPB(CellInfo *ci, std::vector<std::unique_ptr<CellInfo>> &new_cells)
|
||
{
|
||
int default_bw = 32;
|
||
if (ci->type == id_SDPB) {
|
||
ci->setAttr(id_BSRAM_SUBTYPE, Property(""));
|
||
} else {
|
||
ci->setAttr(id_BSRAM_SUBTYPE, Property("X9"));
|
||
default_bw = 36;
|
||
}
|
||
|
||
if (!ci->params.count(id_BIT_WIDTH_0)) {
|
||
ci->setParam(id_BIT_WIDTH_0, Property(default_bw, 32));
|
||
}
|
||
if (!ci->params.count(id_BIT_WIDTH_1)) {
|
||
ci->setParam(id_BIT_WIDTH_1, Property(default_bw, 32));
|
||
}
|
||
|
||
int bit_width = ci->params.at(id_BIT_WIDTH_0).as_int64();
|
||
|
||
if ((bit_width == 32 || bit_width == 36) && gwu.need_SDP_fix()) {
|
||
int bit_width_b = ci->params.at(id_BIT_WIDTH_1).as_int64();
|
||
if (bit_width == bit_width_b) {
|
||
divide_sdp(ci, new_cells);
|
||
} else {
|
||
log_error("The fix for SDP when ports A and B have different bit widths has not yet been implemented. "
|
||
"Cell: '%s'\n",
|
||
ci->type.c_str(ctx));
|
||
}
|
||
}
|
||
|
||
NetInfo *vcc_net = ctx->nets.at(ctx->id("$PACKER_VCC")).get();
|
||
NetInfo *vss_net = ctx->nets.at(ctx->id("$PACKER_GND")).get();
|
||
|
||
ci->copyPortTo(id_OCE, ci, id_OCEB);
|
||
|
||
// If misconnected RESET
|
||
if (gwu.need_BSRAM_RESET_fix()) {
|
||
ci->renamePort(id_RESET, id_RESETB);
|
||
}
|
||
|
||
// Port A
|
||
ci->addInput(id_WREA);
|
||
ci->connectPort(id_WREA, vcc_net);
|
||
|
||
// Port B
|
||
ci->addInput(id_WREB);
|
||
bit_width = ci->params.at(id_BIT_WIDTH_1).as_int64();
|
||
|
||
if (bit_width == 32 || bit_width == 36) {
|
||
ci->connectPort(id_WREB, vcc_net);
|
||
bsram_rename_ports(ci, bit_width, "DO[%d]", "DO%d");
|
||
} else {
|
||
ci->connectPort(id_WREB, vss_net);
|
||
bsram_rename_ports(ci, bit_width, "DO[%d]", "DO%d", 18);
|
||
}
|
||
bsram_rename_ports(ci, bit_width, "DI[%d]", "DI%d");
|
||
gwu.remove_brackets(ci);
|
||
}
|
||
|
||
void GowinPacker::pack_DPB(CellInfo *ci, std::vector<std::unique_ptr<CellInfo>> &new_cells)
|
||
{
|
||
int default_bw = 16;
|
||
if (ci->type == id_DPB) {
|
||
ci->setAttr(id_BSRAM_SUBTYPE, Property(""));
|
||
} else {
|
||
ci->setAttr(id_BSRAM_SUBTYPE, Property("X9"));
|
||
default_bw = 18;
|
||
}
|
||
|
||
if (!ci->params.count(id_BIT_WIDTH_0)) {
|
||
ci->setParam(id_BIT_WIDTH_0, Property(default_bw, 32));
|
||
}
|
||
int bit_width = ci->params.at(id_BIT_WIDTH_0).as_int64();
|
||
bsram_rename_ports(ci, bit_width, "DIA[%d]", "DIA%d");
|
||
|
||
if (bit_width < 16 && gwu.need_BSRAM_OUTREG_fix()) {
|
||
bsram_fix_outreg(ci, bit_width, id_CEA, id_OCEA, id_CLKA, id_RESETA, id_DOA, id_READ_MODE0, new_cells);
|
||
}
|
||
bsram_rename_ports(ci, bit_width, "DOA[%d]", "DOA%d");
|
||
// In BYPASS mode, the OCE signal is dictated by CE.
|
||
if (gwu.need_BSRAM_DP_CE_fix()) {
|
||
if (bit_width <= 9) {
|
||
ci->disconnectPort(id_OCEA);
|
||
ci->copyPortTo(id_CEA, ci, id_OCEA);
|
||
}
|
||
}
|
||
if (!ci->params.count(id_BIT_WIDTH_1)) {
|
||
ci->setParam(id_BIT_WIDTH_1, Property(default_bw, 32));
|
||
}
|
||
bit_width = ci->params.at(id_BIT_WIDTH_1).as_int64();
|
||
bsram_rename_ports(ci, bit_width, "DIB[%d]", "DIB%d");
|
||
|
||
if (bit_width < 16 && gwu.need_BSRAM_OUTREG_fix()) {
|
||
bsram_fix_outreg(ci, bit_width, id_CEB, id_OCEB, id_CLKB, id_RESETB, id_DOB, id_READ_MODE1, new_cells);
|
||
}
|
||
bsram_rename_ports(ci, bit_width, "DOB[%d]", "DOB%d");
|
||
|
||
// In BYPASS mode, the OCE signal is dictated by CE.
|
||
if (gwu.need_BSRAM_DP_CE_fix()) {
|
||
if (bit_width <= 9) {
|
||
ci->disconnectPort(id_OCEB);
|
||
ci->copyPortTo(id_CEB, ci, id_OCEB);
|
||
}
|
||
}
|
||
|
||
gwu.remove_brackets(ci);
|
||
}
|
||
|
||
void GowinPacker::divide_sp(CellInfo *ci, std::vector<std::unique_ptr<CellInfo>> &new_cells)
|
||
{
|
||
if (ctx->verbose) {
|
||
log_info(" divide SP\n");
|
||
}
|
||
|
||
int bw = ci->params.at(id_BIT_WIDTH).as_int64();
|
||
NetInfo *vcc_net = ctx->nets.at(ctx->id("$PACKER_VCC")).get();
|
||
NetInfo *vss_net = ctx->nets.at(ctx->id("$PACKER_GND")).get();
|
||
|
||
IdString cell_type = bw == 32 ? id_SP : id_SPX9;
|
||
IdString name = ctx->idf("%s_AUX", ctx->nameOf(ci));
|
||
|
||
auto sp_cell = gwu.create_cell(name, cell_type);
|
||
CellInfo *sp = sp_cell.get();
|
||
sp->setAttr(id_AUX, 1);
|
||
|
||
ci->copyPortTo(id_CLK, sp, id_CLK);
|
||
ci->copyPortTo(id_OCE, sp, id_OCE);
|
||
ci->copyPortTo(id_CE, sp, id_CE);
|
||
ci->copyPortTo(id_RESET, sp, id_RESET);
|
||
ci->copyPortTo(id_WRE, sp, id_WRE);
|
||
|
||
// XXX Separate "byte enable" port
|
||
ci->movePortTo(ctx->id("AD[2]"), sp, ctx->id("AD[0]"));
|
||
ci->movePortTo(ctx->id("AD[3]"), sp, ctx->id("AD[1]"));
|
||
ci->connectPort(ctx->id("AD[2]"), vss_net);
|
||
ci->connectPort(ctx->id("AD[3]"), vss_net);
|
||
|
||
sp->addInput(ctx->id("AD[2]"));
|
||
sp->connectPort(ctx->id("AD[2]"), vss_net);
|
||
sp->addInput(ctx->id("AD[3]"));
|
||
sp->connectPort(ctx->id("AD[3]"), vss_net);
|
||
|
||
ci->disconnectPort(ctx->id("AD[4]"));
|
||
ci->connectPort(ctx->id("AD[4]"), vss_net);
|
||
sp->addInput(ctx->id("AD[4]"));
|
||
sp->connectPort(ctx->id("AD[4]"), vcc_net);
|
||
|
||
ci->copyPortBusTo(id_AD, 5, true, sp, id_AD, 5, true, 9);
|
||
|
||
sp->params = ci->params;
|
||
|
||
bw /= 2;
|
||
ci->setParam(id_BIT_WIDTH, Property(bw, 32));
|
||
sp->setParam(id_BIT_WIDTH, Property(bw, 32));
|
||
ci->movePortBusTo(id_DI, bw, true, sp, id_DI, 0, true, bw);
|
||
ci->movePortBusTo(id_DO, bw, true, sp, id_DO, 0, true, bw);
|
||
|
||
ci->copyPortBusTo(ctx->id("BLKSEL"), 0, true, sp, ctx->id("BLKSEL"), 0, true, 3);
|
||
|
||
new_cells.push_back(std::move(sp_cell));
|
||
}
|
||
|
||
void GowinPacker::pack_SP(CellInfo *ci, std::vector<std::unique_ptr<CellInfo>> &new_cells)
|
||
{
|
||
int default_bw = 32;
|
||
if (ci->type == id_SP) {
|
||
ci->setAttr(id_BSRAM_SUBTYPE, Property(""));
|
||
} else {
|
||
ci->setAttr(id_BSRAM_SUBTYPE, Property("X9"));
|
||
default_bw = 36;
|
||
}
|
||
if (!ci->params.count(id_BIT_WIDTH)) {
|
||
ci->setParam(id_BIT_WIDTH, Property(default_bw, 32));
|
||
}
|
||
|
||
int bit_width = ci->params.at(id_BIT_WIDTH).as_int64();
|
||
|
||
if (!ci->attrs.count(id_AUX)) {
|
||
// XXX strange WRE<->CE relations
|
||
// Gowin IDE adds two LUTs to the WRE and CE signals. The logic is
|
||
// unclear, but without them effects occur. Perhaps this is a
|
||
// correction of some BSRAM defects.
|
||
if (gwu.need_SP_fix()) {
|
||
bsram_fix_sp(ci, new_cells);
|
||
}
|
||
|
||
// Some chips have problems with BLKSEL ports
|
||
if (gwu.need_BLKSEL_fix()) {
|
||
bsram_fix_blksel(ci, new_cells);
|
||
}
|
||
|
||
// Some chips have faulty output registers
|
||
if (bit_width < 32 && gwu.need_BSRAM_OUTREG_fix()) {
|
||
bsram_fix_outreg(ci, bit_width, id_CE, id_OCE, id_CLK, id_RESET, id_DO, id_READ_MODE, new_cells);
|
||
}
|
||
}
|
||
|
||
// XXX UG285-1.3.6_E Gowin BSRAM & SSRAM User Guide:
|
||
// For GW1N-9/GW1NR-9/GW1NS-4 series, 32/36-bit SP/SPX9 is divided into two
|
||
// SP/SPX9s, which occupy two BSRAMs.
|
||
// So divide it here
|
||
if ((bit_width == 32 || bit_width == 36) && !gwu.has_SP32()) {
|
||
divide_sp(ci, new_cells);
|
||
bit_width = ci->params.at(id_BIT_WIDTH).as_int64();
|
||
}
|
||
|
||
NetInfo *vcc_net = ctx->nets.at(ctx->id("$PACKER_VCC")).get();
|
||
NetInfo *gnd_net = ctx->nets.at(ctx->id("$PACKER_GND")).get();
|
||
for (int i = 0; i < 3; ++i) {
|
||
ci->renamePort(ctx->idf("BLKSEL[%d]", i), ctx->idf("BLKSEL%d", i));
|
||
if (bit_width == 32 || bit_width == 36) {
|
||
ci->copyPortTo(ctx->idf("BLKSEL%d", i), ci, ctx->idf("BLKSELB%d", i));
|
||
}
|
||
}
|
||
|
||
for (int i = 0; i < 14; ++i) {
|
||
ci->renamePort(ctx->idf("AD[%d]", i), ctx->idf("AD%d", i));
|
||
if (bit_width == 32 || bit_width == 36) {
|
||
// Since we are dividing 32/36 bits into two parts between
|
||
// ports A and B, the ‘Byte Enables’ require special
|
||
// separation.
|
||
if (i < 4) {
|
||
if (i > 1) {
|
||
ci->movePortTo(ctx->idf("AD%d", i), ci, ctx->idf("ADB%d", i - 2));
|
||
ci->connectPort(ctx->idf("AD%d", i), gnd_net);
|
||
ci->addInput(ctx->idf("ADB%d", i));
|
||
ci->connectPort(ctx->idf("ADB%d", i), gnd_net);
|
||
}
|
||
} else {
|
||
ci->copyPortTo(ctx->idf("AD%d", i), ci, ctx->idf("ADB%d", i));
|
||
}
|
||
}
|
||
}
|
||
if (bit_width == 32 || bit_width == 36) {
|
||
ci->copyPortTo(id_CLK, ci, id_CLKB);
|
||
ci->copyPortTo(id_OCE, ci, id_OCEB);
|
||
ci->copyPortTo(id_CE, ci, id_CEB);
|
||
ci->copyPortTo(id_RESET, ci, id_RESETB);
|
||
ci->copyPortTo(id_WRE, ci, id_WREB);
|
||
ci->disconnectPort(ctx->id("AD4"));
|
||
ci->connectPort(ctx->id("AD4"), gnd_net);
|
||
ci->disconnectPort(ctx->id("ADB4"));
|
||
ci->connectPort(ctx->id("ADB4"), vcc_net);
|
||
}
|
||
bsram_rename_ports(ci, bit_width, "DI[%d]", "DI%d");
|
||
bsram_rename_ports(ci, bit_width, "DO[%d]", "DO%d");
|
||
}
|
||
|
||
void GowinPacker::pack_bsram(void)
|
||
{
|
||
std::vector<std::unique_ptr<CellInfo>> new_cells;
|
||
log_info("Pack BSRAMs...\n");
|
||
|
||
auto do_bsram = [&](CellInfo *ci) {
|
||
if (ctx->verbose) {
|
||
log_info(" pack %s\n", ci->type.c_str(ctx));
|
||
}
|
||
switch (ci->type.hash()) {
|
||
case ID_pROMX9: /* fallthrough */
|
||
case ID_pROM:
|
||
pack_ROM(ci);
|
||
ci->type = id_ROM;
|
||
break;
|
||
case ID_SDPX9B: /* fallthrough */
|
||
case ID_SDPB:
|
||
pack_SDPB(ci, new_cells);
|
||
ci->type = id_SDP;
|
||
break;
|
||
case ID_DPX9B: /* fallthrough */
|
||
case ID_DPB:
|
||
pack_DPB(ci, new_cells);
|
||
ci->type = id_DP;
|
||
break;
|
||
case ID_SPX9: /* fallthrough */
|
||
case ID_SP:
|
||
pack_SP(ci, new_cells);
|
||
ci->type = id_SP;
|
||
break;
|
||
default:
|
||
log_error("Unsupported BSRAM type '%s'\n", ci->type.c_str(ctx));
|
||
}
|
||
};
|
||
|
||
for (auto &cell : ctx->cells) {
|
||
auto ci = cell.second.get();
|
||
if (is_bsram(ci)) {
|
||
do_bsram(ci);
|
||
}
|
||
}
|
||
|
||
// Process new cells. New cells should not generate more.
|
||
for (auto &cell : new_cells) {
|
||
auto ci = cell.get();
|
||
if (is_bsram(ci)) {
|
||
do_bsram(ci);
|
||
}
|
||
ctx->cells[cell->name] = std::move(cell);
|
||
}
|
||
}
|
||
NEXTPNR_NAMESPACE_END
|