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:
parent
a29a17f8f2
commit
8f87918230
@ -890,6 +890,8 @@ X(WRE)
|
|||||||
|
|
||||||
// BSRAM
|
// BSRAM
|
||||||
X(BSRAM_SUBTYPE)
|
X(BSRAM_SUBTYPE)
|
||||||
|
X(WRITE_MODE)
|
||||||
|
X(READ_MODE)
|
||||||
X(BIT_WIDTH)
|
X(BIT_WIDTH)
|
||||||
X(BIT_WIDTH_0)
|
X(BIT_WIDTH_0)
|
||||||
X(BIT_WIDTH_1)
|
X(BIT_WIDTH_1)
|
||||||
|
@ -259,6 +259,7 @@ void GowinImpl::adjust_dsp_pin_mapping(void)
|
|||||||
void GowinImpl::prePlace() { assign_cell_info(); }
|
void GowinImpl::prePlace() { assign_cell_info(); }
|
||||||
void GowinImpl::postPlace()
|
void GowinImpl::postPlace()
|
||||||
{
|
{
|
||||||
|
gwu.have_SP32();
|
||||||
if (ctx->debug) {
|
if (ctx->debug) {
|
||||||
log_info("================== Final Placement ===================\n");
|
log_info("================== Final Placement ===================\n");
|
||||||
for (auto &cell : ctx->cells) {
|
for (auto &cell : ctx->cells) {
|
||||||
|
@ -95,7 +95,8 @@ NPNR_PACKED_STRUCT(struct Extra_chip_data_POD {
|
|||||||
Bottom_io_POD bottom_io;
|
Bottom_io_POD bottom_io;
|
||||||
RelSlice<IdString> diff_io_types;
|
RelSlice<IdString> diff_io_types;
|
||||||
// chip flags
|
// 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
|
} // namespace
|
||||||
|
@ -16,6 +16,7 @@ BEL_FLAG_SIMPLE_IO = 0x100
|
|||||||
|
|
||||||
# Chip flags
|
# Chip flags
|
||||||
CHIP_HAS_SP32 = 0x1
|
CHIP_HAS_SP32 = 0x1
|
||||||
|
CHIP_NEED_SP_FIX = 0x2
|
||||||
|
|
||||||
# Z of the bels
|
# Z of the bels
|
||||||
# sync with C++ part!
|
# sync with C++ part!
|
||||||
@ -1008,8 +1009,15 @@ def main():
|
|||||||
db = pickle.load(f)
|
db = pickle.load(f)
|
||||||
|
|
||||||
chip_flags = 0;
|
chip_flags = 0;
|
||||||
|
# XXX compatibility
|
||||||
|
if not hasattr(db, "chip_flags"):
|
||||||
if device not in {"GW1NS-4", "GW1N-9"}:
|
if device not in {"GW1NS-4", "GW1N-9"}:
|
||||||
chip_flags &= CHIP_HAS_SP32;
|
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;
|
X = db.cols;
|
||||||
Y = db.rows;
|
Y = db.rows;
|
||||||
|
@ -118,6 +118,12 @@ bool GowinUtils::have_SP32(void)
|
|||||||
return extra->chip_flags & Extra_chip_data_POD::HAS_SP32;
|
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)
|
std::unique_ptr<CellInfo> GowinUtils::create_cell(IdString name, IdString type)
|
||||||
{
|
{
|
||||||
NPNR_ASSERT(!ctx->cells.count(name));
|
NPNR_ASSERT(!ctx->cells.count(name));
|
||||||
|
@ -34,6 +34,10 @@ struct GowinUtils
|
|||||||
Loc get_pair_iologic_bel(Loc loc);
|
Loc get_pair_iologic_bel(Loc loc);
|
||||||
BelId get_io_bel_from_iologic(BelId bel);
|
BelId get_io_bel_from_iologic(BelId bel);
|
||||||
|
|
||||||
|
// BSRAM
|
||||||
|
bool have_SP32(void);
|
||||||
|
bool need_SP_fix(void);
|
||||||
|
|
||||||
// DSP
|
// DSP
|
||||||
inline int get_dsp_18_z(int z) const { return z & (~3); }
|
inline int get_dsp_18_z(int z) const { return z & (~3); }
|
||||||
inline int get_dsp_9_idx(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));
|
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.
|
// make cell but do not include it in the list of chip cells.
|
||||||
std::unique_ptr<CellInfo> create_cell(IdString name, IdString type);
|
std::unique_ptr<CellInfo> create_cell(IdString name, IdString type);
|
||||||
};
|
};
|
||||||
|
@ -508,13 +508,13 @@ struct GowinPacker
|
|||||||
make_iob_nets(*out_iob);
|
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("");
|
std::string sfx("");
|
||||||
if (idx) {
|
if (idx) {
|
||||||
sfx = std::to_string(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)
|
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
|
// Analysis of the images generated by the IDE showed that some components
|
||||||
// also respond to this signal, unless the OCE is controlled separately.
|
// are being added at the input and output of the BSRAM. Two LUTs are
|
||||||
void bsram_handle_sp_oce(CellInfo *ci, IdString ce_pin, IdString oce_pin)
|
// 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);
|
int bit_width = ci->params.at(id_BIT_WIDTH).as_int64();
|
||||||
NPNR_ASSERT(ci->getPort(ce_pin) != nullptr);
|
|
||||||
if (net == nullptr || net->name == ctx->id("$PACKER_VCC") || net->name == ctx->id("$PACKER_GND")) {
|
// 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) {
|
if (net != nullptr) {
|
||||||
ci->disconnectPort(oce_pin);
|
if (net->users.empty()) {
|
||||||
|
ci->disconnectPort(do_name);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
ci->copyPortTo(ce_pin, ci, oce_pin);
|
// 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));
|
||||||
}
|
}
|
||||||
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)
|
void pack_ROM(CellInfo *ci)
|
||||||
@ -1432,7 +1513,6 @@ struct GowinPacker
|
|||||||
ci->renamePort(ctx->idf("BLKSELB[%d]", i), ctx->idf("BLKSELB%d", i));
|
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);
|
ci->copyPortTo(id_OCE, ci, id_OCEB);
|
||||||
|
|
||||||
// Port A
|
// Port A
|
||||||
@ -1565,7 +1645,14 @@ struct GowinPacker
|
|||||||
}
|
}
|
||||||
|
|
||||||
int bit_width = ci->params.at(id_BIT_WIDTH).as_int64();
|
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:
|
// 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
|
// 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();
|
pack_gsr();
|
||||||
ctx->check();
|
ctx->check();
|
||||||
|
|
||||||
pack_inv();
|
|
||||||
ctx->check();
|
|
||||||
|
|
||||||
pack_wideluts();
|
pack_wideluts();
|
||||||
ctx->check();
|
ctx->check();
|
||||||
|
|
||||||
@ -2751,6 +2835,9 @@ struct GowinPacker
|
|||||||
pack_dsp();
|
pack_dsp();
|
||||||
ctx->check();
|
ctx->check();
|
||||||
|
|
||||||
|
pack_inv();
|
||||||
|
ctx->check();
|
||||||
|
|
||||||
pack_buffered_nets();
|
pack_buffered_nets();
|
||||||
|
|
||||||
ctx->fixupHierarchy();
|
ctx->fixupHierarchy();
|
||||||
|
Loading…
Reference in New Issue
Block a user