GPIO initial work

This commit is contained in:
Miodrag Milanovic 2024-12-20 09:38:14 +01:00
parent 2b038faeb1
commit 757ed10047
8 changed files with 264 additions and 22 deletions

View File

@ -101,14 +101,17 @@ struct BitstreamBackend
cc.configs[0].add_word("GPIO.BANK_W1", int_to_bitvector(1, 1));
cc.configs[0].add_word("GPIO.BANK_W2", int_to_bitvector(1, 1));
for (auto &cell : ctx->cells) {
CfgLoc loc = getConfigLoc(ctx, cell.second.get()->bel.tile);
auto &params = cell.second.get()->params;
switch (cell.second->type.index) {
case id_GPIO.index: {
CfgLoc loc = getConfigLoc(ctx, cell.second.get()->bel.tile);
cc.tiles[loc].add_word(
"GPIO.INIT",
str_to_bitvector(str_or_default(cell.second.get()->params, ctx->id("INIT"), ""), 72));
case id_CC_IBUF.index:
case id_CC_TOBUF.index:
case id_CC_OBUF.index:
case id_CC_IOBUF.index:
for (auto &p : params) {
cc.tiles[loc].add_word(stringf("GPIO.%s", p.first.c_str(ctx)), p.second.as_bits());
}
break;
}
default:
break;
}

View File

@ -77,7 +77,7 @@ struct GateMateCCFReader
log_error("Value '%s' can not be defined for default GPIO in line %d.\n", name.c_str(), lineno);
if (ctx->get_package_pin_bel(ctx->id(value)) == BelId())
log_error("Unknown location '%s' used in line %d.\n", value.c_str(), lineno);
props->emplace(ctx->id(name.c_str()), Property(value));
props->emplace(ctx->id("PIN_NAME"), Property(value));
} else if (name == "SCHMITT_TRIGGER" || name == "PULLUP" || name == "PULLDOWN" || name == "KEEPER" ||
name == "FF_IBF" || name == "FF_OBF" || name == "LVDS_BOOST" || name == "LVDS_RTERM") {
if (value == "TRUE") {

View File

@ -46,11 +46,50 @@ X(CC_IDDR)
X(CC_ODDR)
X(I)
X(O)
X(IO)
X(DI)
X(DO)
X(OE)
X(Y)
X(A)
X(T)
X(I_P)
X(I_N)
X(O_P)
X(O_N)
X(IO_P)
X(IO_N)
X(OUT3)
X(OUT4)
X(PIN_NAME)
X(PIN_NAME_P)
X(PIN_NAME_N)
X(V_IO)
X(PULLUP)
X(PULLDOWN)
X(KEEPER)
X(SCHMITT_TRIGGER)
X(DELAY_IBF)
X(FF_IBF)
X(DRIVE)
X(SLEW)
X(DELAY_OBF)
X(FF_OBF)
X(LVDS_RTERM)
X(LVDS_BOOST)
X(OE_ENABLE)
X(OUT_SIGNAL)
X(INPUT_ENABLE)
X(OUT23_14_SEL)
X(INIT_L00)
X(INIT_L01)
X(INIT_L02)
X(INIT_L03)
X(INIT_L10)
X(INIT_L11)
X(INIT_L20)
X(INIT_L30)

View File

@ -65,6 +65,24 @@ void GateMateImpl::setupArchContext()
}
}
// Bel bucket functions
IdString GateMateImpl::getBelBucketForCellType(IdString cell_type) const
{
if (cell_type.in(id_CC_IBUF, id_CC_IBUF, id_CC_TOBUF, id_CC_IOBUF))
return id_GPIO;
else
return cell_type;
}
bool GateMateImpl::isValidBelForCellType(IdString cell_type, BelId bel) const
{
IdString bel_type = ctx->getBelType(bel);
if (bel_type == id_GPIO)
return cell_type.in(id_CC_IBUF, id_CC_IBUF, id_CC_TOBUF, id_CC_IOBUF);
else
return (bel_type == cell_type);
}
struct GateMateArch : HimbaechelArch
{
GateMateArch() : HimbaechelArch("gatemate") {};

View File

@ -50,6 +50,9 @@ struct GateMateImpl : HimbaechelAPI
bool read_bitstream(const std::string &device, const std::string &filename);
void parse_ccf(const std::string &filename);
IdString getBelBucketForCellType(IdString cell_type) const;
bool isValidBelForCellType(IdString cell_type, BelId bel) const;
};
NEXTPNR_NAMESPACE_END

View File

@ -81,6 +81,76 @@ def main():
tt.create_pip("CPE.IN1", "CPE.OUT2")
tt.create_pip("CPE.IN1", "CPE.RAM_O1")
tt.create_pip("CPE.IN1", "CPE.RAM_O2")
if "GPIO" in type_name:
tt.create_wire("GPIO.OUT_D1", "WIRE_INTERNAL")
tt.create_wire("GPIO.OUT_D2", "WIRE_INTERNAL")
tt.create_wire("GPIO.OUT_Q1", "WIRE_INTERNAL")
tt.create_wire("GPIO.OUT_Q2", "WIRE_INTERNAL")
tt.create_wire("GPIO.OUT_CLK","WIRE_INTERNAL")
tt.create_wire("GPIO.CLK_INT","WIRE_INTERNAL")
pp = tt.create_pip("GPIO.OUT1", "GPIO.OUT_D1")
pp.extra_data = PipExtraData(ch.strs.id("GPIO.OUT1_4"), 1, 0, False)
pp = tt.create_pip("GPIO.OUT4", "GPIO.OUT_D1")
pp.extra_data = PipExtraData(ch.strs.id("GPIO.OUT1_4"), 1, 1, False)
pp = tt.create_pip("GPIO.OUT2", "GPIO.OUT_D2")
pp.extra_data = PipExtraData(ch.strs.id("GPIO.OUT2_3"), 1, 0, False)
pp = tt.create_pip("GPIO.OUT3", "GPIO.OUT_D2")
pp.extra_data = PipExtraData(ch.strs.id("GPIO.OUT2_3"), 1, 1, False)
pp = tt.create_pip("GPIO.OUT_D1","GPIO.DO")
pp.extra_data = PipExtraData(ch.strs.id("GPIO.OUT23_14_SEL"), 1, 0, False)
pp = tt.create_pip("GPIO.OUT_D2","GPIO.DO")
pp.extra_data = PipExtraData(ch.strs.id("GPIO.OUT23_14_SEL"), 1, 1, False)
pp = tt.create_pip("GPIO.OUT2","GPIO.OE")
pp.extra_data = PipExtraData(ch.strs.id("GPIO.OE_SIGNAL"), 2, 1, False)
pp = tt.create_pip("GPIO.OUT3","GPIO.OE")
pp.extra_data = PipExtraData(ch.strs.id("GPIO.OE_SIGNAL"), 2, 2, False)
pp = tt.create_pip("GPIO.OUT4","GPIO.OE")
pp.extra_data = PipExtraData(ch.strs.id("GPIO.OE_SIGNAL"), 2, 3, False)
pp = tt.create_pip("GPIO.OUT4", "GPIO.CLK_INT")
pp.extra_data = PipExtraData(ch.strs.id("GPIO.CLK_1_4"), 1, 0, False)
pp = tt.create_pip("GPIO.OUT1", "GPIO.CLK_INT")
pp.extra_data = PipExtraData(ch.strs.id("GPIO.CLK_1_4"), 1, 1, False)
pp = tt.create_pip("GPIO.CLK_INT", "GPIO.OUT_CLK")
pp.extra_data = PipExtraData(ch.strs.id("GPIO.SEL_OUT_CLOCK"), 1, 1, False)
pp = tt.create_pip("GPIO.CLOCK1", "GPIO.OUT_CLK")
pp.extra_data = PipExtraData(ch.strs.id("GPIO.OUT_CLOCK"), 2, 0, False)
pp = tt.create_pip("GPIO.CLOCK2", "GPIO.OUT_CLK")
pp.extra_data = PipExtraData(ch.strs.id("GPIO.OUT_CLOCK"), 2, 1, False)
pp = tt.create_pip("GPIO.CLOCK3", "GPIO.OUT_CLK")
pp.extra_data = PipExtraData(ch.strs.id("GPIO.OUT_CLOCK"), 2, 2, False)
pp = tt.create_pip("GPIO.CLOCK4", "GPIO.OUT_CLK")
pp.extra_data = PipExtraData(ch.strs.id("GPIO.OUT_CLOCK"), 2, 3, False)
tt.create_wire("GPIO.IN_D1", "WIRE_INTERNAL")
tt.create_wire("GPIO.IN_D2", "WIRE_INTERNAL")
tt.create_wire("GPIO.IN_Q1", "WIRE_INTERNAL")
tt.create_wire("GPIO.IN_Q2", "WIRE_INTERNAL")
tt.create_wire("GPIO.IN_CLK","WIRE_INTERNAL")
pp = tt.create_pip("GPIO.CLK_INT", "GPIO.IN_CLK")
pp.extra_data = PipExtraData(ch.strs.id("GPIO.SEL_IN_CLOCK"), 1, 1, False)
pp = tt.create_pip("GPIO.CLOCK1", "GPIO.IN_CLK")
pp.extra_data = PipExtraData(ch.strs.id("GPIO.IN_CLOCK"), 2, 0, False)
pp = tt.create_pip("GPIO.CLOCK2", "GPIO.IN_CLK")
pp.extra_data = PipExtraData(ch.strs.id("GPIO.IN_CLOCK"), 2, 1, False)
pp = tt.create_pip("GPIO.CLOCK3", "GPIO.IN_CLK")
pp.extra_data = PipExtraData(ch.strs.id("GPIO.IN_CLOCK"), 2, 2, False)
pp = tt.create_pip("GPIO.CLOCK4", "GPIO.IN_CLK")
pp.extra_data = PipExtraData(ch.strs.id("GPIO.IN_CLOCK"), 2, 3, False)
tt.create_pip("GPIO.DI", "GPIO.IN1")
tt.create_pip("GPIO.DI", "GPIO.IN2")
# Setup tile grid
for x in range(die.max_col() + 3):

View File

@ -24,6 +24,15 @@
NEXTPNR_NAMESPACE_BEGIN
void GateMatePacker::disconnect_if_gnd(CellInfo *cell, IdString input)
{
NetInfo *net = cell->getPort(input);
if (!net)
return;
if (net->name.in(ctx->id("$PACKER_GND"))) {
cell->disconnectPort(input);
}
}
void GateMatePacker::pack_io()
{
// Trim nextpnr IOBs - assume IO buffer insertion has been done in synthesis
@ -80,8 +89,15 @@ void GateMatePacker::pack_io()
// Copy attributes to real IO buffer
for (auto &attrs : ci->attrs)
top_port.cell->attrs[attrs.first] = attrs.second;
for (auto &params : ci->params)
for (auto &params : ci->params) {
if (top_port.cell->params.count(params.first)) {
if (top_port.cell->params[params.first] != params.second) {
log_warning("Overriding parameter '%s' with value '%s' for cell '%s'.\n",
params.first.c_str(ctx), params.second.c_str(), ctx->nameOf(top_port.cell));
}
}
top_port.cell->params[params.first] = params.second;
}
// Make sure that top level net is set correctly
port.second.net = top_port.cell->ports.at(top_port.port).net;
@ -94,26 +110,72 @@ void GateMatePacker::pack_io()
for (auto &cell : ctx->cells) {
CellInfo &ci = *cell.second;
if (!ci.type.in(id_CC_IBUF, id_CC_OBUF))
if (!ci.type.in(id_CC_IBUF, id_CC_OBUF, id_CC_TOBUF, id_CC_IOBUF))
continue;
std::string loc;
if (ci.params.count(ctx->id("LOC")) == 0) {
if (ci.params.count(ctx->id("PIN_NAME")) == 0) {
log_warning("IO signal name '%s' is not defined in CCF file and will be auto-placed.\n", ctx->nameOf(&ci));
} else {
loc = ci.params.at(ctx->id("LOC")).to_string();
loc = ci.params.at(ctx->id("PIN_NAME")).to_string();
}
if (ci.type == id_CC_IBUF) {
ci.renamePort(id_I, id_DI);
ci.renamePort(id_Y, id_IN1);
ci.params[ctx->id("INIT")] =
Property("000000000000000000000001000000000000000000000000000000000000000001010000");
disconnect_if_gnd(&ci, id_T);
if (ci.type == id_CC_TOBUF && !ci.getPort(id_T))
ci.type = id_CC_OBUF;
std::vector<IdString> keys;
for (auto &p : ci.params) {
if (p.first.in(id_PIN_NAME, id_V_IO)) {
keys.push_back(p.first);
continue;
}
if (ci.type.in(id_CC_IBUF, id_CC_IOBUF) &&
p.first.in(id_PULLUP, id_PULLDOWN, id_KEEPER, id_SCHMITT_TRIGGER, id_DELAY_IBF, id_FF_IBF))
continue;
if (ci.type.in(id_CC_TOBUF) && p.first.in(id_PULLUP, id_PULLDOWN, id_KEEPER))
continue;
if (ci.type.in(id_CC_OBUF, id_CC_TOBUF, id_CC_IOBUF) &&
p.first.in(id_DRIVE, id_SLEW, id_DELAY_OBF, id_FF_OBF))
continue;
log_warning("Removing unsupported parameter '%s' for type '%s'.\n", p.first.c_str(ctx), ci.type.c_str(ctx));
keys.push_back(p.first);
}
if (ci.type == id_CC_OBUF) {
ci.renamePort(id_O, id_DO);
ci.renamePort(id_A, id_OUT2);
ci.params[ctx->id("INIT")] =
Property("000000000000000000000000000000000000000100000000000000010000100100000000");
for (auto key : keys)
ci.params.erase(key);
if ((ci.params.count(id_KEEPER) + ci.params.count(id_PULLUP) + ci.params.count(id_PULLDOWN)) > 1)
log_error("PULLUP, PULLDOWN and KEEPER are mutually exclusive parameters.\n");
// DELAY_IBF and DELAY_OBF must be set depending of type
// Also we need to enable input/output
if (ci.type.in(id_CC_IBUF, id_CC_IOBUF)) {
ci.params[id_DELAY_IBF] = Property(1 << int_or_default(ci.params, id_DELAY_IBF, 0), 16);
ci.params[id_INPUT_ENABLE] = Property(Property::State::S1);
}
if (ci.type.in(id_CC_OBUF, id_CC_TOBUF, id_CC_IOBUF)) {
ci.params[id_DELAY_OBF] = Property(1 << int_or_default(ci.params, id_DELAY_OBF, 0), 16);
ci.params[id_OE_ENABLE] = Property(Property::State::S1);
}
// Disconnect PADs
ci.disconnectPort(id_IO);
ci.disconnectPort(id_I);
ci.disconnectPort(id_O);
// Remap ports to GPIO bel
ci.renamePort(id_A, id_DO);
ci.renamePort(id_Y, id_DI);
ci.renamePort(id_T, id_OE);
NetInfo *do_net = ci.getPort(id_DO);
if (do_net) {
if (do_net->name.in(ctx->id("$PACKER_GND"), ctx->id("$PACKER_VCC"))) {
ci.params[id_OUT23_14_SEL] =
Property(do_net->name == ctx->id("$PACKER_VCC") ? Property::State::S1 : Property::State::S0);
ci.disconnectPort(id_DO);
} else {
ci.params[id_OUT_SIGNAL] = Property(Property::State::S1);
}
}
if (!loc.empty()) {
BelId bel = ctx->get_package_pin_bel(ctx->id(loc));
@ -124,9 +186,50 @@ void GateMatePacker::pack_io()
ctx->bindBel(bel, &ci, PlaceStrength::STRENGTH_FIXED);
}
ci.type = id_GPIO;
}
}
void GateMatePacker::pack_constants()
{
log_info("Packing constants..\n");
// Replace constants with LUTs
const dict<IdString, Property> vcc_params = {
{id_INIT_L00, Property(0xf, 4)}, {id_INIT_L01, Property(0xf, 4)}, {id_INIT_L02, Property(0xf, 4)}};
const dict<IdString, Property> gnd_params = {
{id_INIT_L00, Property(0x0, 4)}, {id_INIT_L01, Property(0x0, 4)}, {id_INIT_L02, Property(0x0, 4)}};
h.replace_constants(CellTypePort(id_CPE, id_OUT1), CellTypePort(id_CPE, id_OUT1), vcc_params, gnd_params);
}
void GateMatePacker::remove_constants()
{
log_info("Removing constants..\n");
auto fnd_cell = ctx->cells.find(ctx->id("$PACKER_VCC_DRV"));
if (fnd_cell != ctx->cells.end()) {
auto fnd_net = ctx->nets.find(ctx->id("$PACKER_VCC"));
if (fnd_net != ctx->nets.end() && fnd_net->second->users.entries() == 0) {
BelId bel = (*fnd_cell).second.get()->bel;
if (bel != BelId())
ctx->unbindBel(bel);
ctx->cells.erase(fnd_cell);
ctx->nets.erase(fnd_net);
log_info(" Removed unused VCC cell\n");
}
}
fnd_cell = ctx->cells.find(ctx->id("$PACKER_GND_DRV"));
if (fnd_cell != ctx->cells.end()) {
auto fnd_net = ctx->nets.find(ctx->id("$PACKER_GND"));
if (fnd_net != ctx->nets.end() && fnd_net->second->users.entries() == 0) {
BelId bel = (*fnd_cell).second.get()->bel;
if (bel != BelId())
ctx->unbindBel(bel);
ctx->cells.erase(fnd_cell);
ctx->nets.erase(fnd_net);
log_info(" Removed unused GND cell\n");
}
}
}
void GateMateImpl::pack()
{
const ArchArgs &args = ctx->args;
@ -135,7 +238,9 @@ void GateMateImpl::pack()
}
GateMatePacker packer(ctx, this);
packer.pack_constants();
packer.pack_io();
packer.remove_constants();
}
NEXTPNR_NAMESPACE_END

View File

@ -29,6 +29,10 @@ struct GateMatePacker
GateMatePacker(Context *ctx, GateMateImpl *uarch) : ctx(ctx), uarch(uarch) { h.init(ctx); };
void pack_io();
void pack_constants();
void disconnect_if_gnd(CellInfo *cell, IdString input);
void remove_constants();
Context *ctx;
GateMateImpl *uarch;