Gowin. Add fix for Single Port BSRAM

Add description of BSRAM harness

In some cases, Gowin IDE adds a number of LUTs and DFFs to the BSRAM. Here we are trying to add similar elements.

More details with pictures: https://github.com/YosysHQ/apicula/blob/master/doc/bsram-fix.md

Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
This commit is contained in:
YRabbit 2024-06-23 20:26:50 +10:00 committed by myrtle
parent a29a17f8f2
commit 8f87918230
7 changed files with 132 additions and 26 deletions

View File

@ -890,6 +890,8 @@ X(WRE)
// BSRAM
X(BSRAM_SUBTYPE)
X(WRITE_MODE)
X(READ_MODE)
X(BIT_WIDTH)
X(BIT_WIDTH_0)
X(BIT_WIDTH_1)

View File

@ -259,6 +259,7 @@ void GowinImpl::adjust_dsp_pin_mapping(void)
void GowinImpl::prePlace() { assign_cell_info(); }
void GowinImpl::postPlace()
{
gwu.have_SP32();
if (ctx->debug) {
log_info("================== Final Placement ===================\n");
for (auto &cell : ctx->cells) {

View File

@ -95,7 +95,8 @@ NPNR_PACKED_STRUCT(struct Extra_chip_data_POD {
Bottom_io_POD bottom_io;
RelSlice<IdString> diff_io_types;
// chip flags
static constexpr int32_t HAS_SP32 = 0;
static constexpr int32_t HAS_SP32 = 1;
static constexpr int32_t NEED_SP_FIX = 2;
});
} // namespace

View File

@ -15,7 +15,8 @@ from apycula import chipdb
BEL_FLAG_SIMPLE_IO = 0x100
# Chip flags
CHIP_HAS_SP32 = 0x1
CHIP_HAS_SP32 = 0x1
CHIP_NEED_SP_FIX = 0x2
# Z of the bels
# sync with C++ part!
@ -1008,8 +1009,15 @@ def main():
db = pickle.load(f)
chip_flags = 0;
if device not in {"GW1NS-4", "GW1N-9"}:
chip_flags &= CHIP_HAS_SP32;
# XXX compatibility
if not hasattr(db, "chip_flags"):
if device not in {"GW1NS-4", "GW1N-9"}:
chip_flags |= CHIP_HAS_SP32;
else:
if "HAS_SP32" in db.chip_flags:
chip_flags |= CHIP_HAS_SP32;
if "NEED_SP_FIX" in db.chip_flags:
chip_flags |= CHIP_NEED_SP_FIX;
X = db.cols;
Y = db.rows;

View File

@ -118,6 +118,12 @@ bool GowinUtils::have_SP32(void)
return extra->chip_flags & Extra_chip_data_POD::HAS_SP32;
}
bool GowinUtils::need_SP_fix(void)
{
const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(ctx->chip_info->extra_data.get());
return extra->chip_flags & Extra_chip_data_POD::NEED_SP_FIX;
}
std::unique_ptr<CellInfo> GowinUtils::create_cell(IdString name, IdString type)
{
NPNR_ASSERT(!ctx->cells.count(name));

View File

@ -34,6 +34,10 @@ struct GowinUtils
Loc get_pair_iologic_bel(Loc loc);
BelId get_io_bel_from_iologic(BelId bel);
// BSRAM
bool have_SP32(void);
bool need_SP_fix(void);
// DSP
inline int get_dsp_18_z(int z) const { return z & (~3); }
inline int get_dsp_9_idx(int z) const { return z & 3; }
@ -81,9 +85,6 @@ struct GowinUtils
return is_global_wire(ctx->getPipSrcWire(pip)) || is_global_wire(ctx->getPipDstWire(pip));
}
// chip dependent
bool have_SP32(void);
// make cell but do not include it in the list of chip cells.
std::unique_ptr<CellInfo> create_cell(IdString name, IdString type);
};

View File

@ -508,13 +508,13 @@ struct GowinPacker
make_iob_nets(*out_iob);
}
IdString create_aux_name(IdString main_name, int idx = 0)
IdString create_aux_name(IdString main_name, int idx = 0, const char *str_suffix = "_aux$")
{
std::string sfx("");
if (idx) {
sfx = std::to_string(idx);
}
return ctx->id(main_name.str(ctx) + std::string("_aux$") + sfx);
return ctx->id(main_name.str(ctx) + std::string(str_suffix) + sfx);
}
BelId get_aux_iologic_bel(const CellInfo &ci)
@ -1331,22 +1331,103 @@ struct GowinPacker
}
}
// If the memory is controlled by the CE, then it is logical for the OCE to
// also respond to this signal, unless the OCE is controlled separately.
void bsram_handle_sp_oce(CellInfo *ci, IdString ce_pin, IdString oce_pin)
// 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 bsram_fix_sp(CellInfo *ci, std::vector<std::unique_ptr<CellInfo>> &new_cells)
{
const NetInfo *net = ci->getPort(oce_pin);
NPNR_ASSERT(ci->getPort(ce_pin) != nullptr);
if (net == nullptr || net->name == ctx->id("$PACKER_VCC") || net->name == ctx->id("$PACKER_GND")) {
int bit_width = ci->params.at(id_BIT_WIDTH).as_int64();
// create WRE LUT
auto wre_lut_cell = gwu.create_cell(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(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(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(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) {
ci->disconnectPort(oce_pin);
if (net->users.empty()) {
ci->disconnectPort(do_name);
continue;
}
// create cache lut
auto cache_lut_cell = gwu.create_cell(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(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));
}
ci->copyPortTo(ce_pin, ci, oce_pin);
}
if (ctx->verbose) {
log_info("%s: %s = %s = %s\n", ctx->nameOf(ci), ce_pin.c_str(ctx), oce_pin.c_str(ctx),
ctx->nameOf(ci->getPort(oce_pin)));
}
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 pack_ROM(CellInfo *ci)
@ -1432,7 +1513,6 @@ struct GowinPacker
ci->renamePort(ctx->idf("BLKSELB[%d]", i), ctx->idf("BLKSELB%d", i));
}
bsram_handle_sp_oce(ci, id_CEB, id_OCE);
ci->copyPortTo(id_OCE, ci, id_OCEB);
// Port A
@ -1565,7 +1645,14 @@ struct GowinPacker
}
int bit_width = ci->params.at(id_BIT_WIDTH).as_int64();
bsram_handle_sp_oce(ci, id_CE, id_OCE);
// 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);
}
// 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
@ -2726,9 +2813,6 @@ struct GowinPacker
pack_gsr();
ctx->check();
pack_inv();
ctx->check();
pack_wideluts();
ctx->check();
@ -2751,6 +2835,9 @@ struct GowinPacker
pack_dsp();
ctx->check();
pack_inv();
ctx->check();
pack_buffered_nets();
ctx->fixupHierarchy();