GPIO initial work
This commit is contained in:
parent
2b038faeb1
commit
757ed10047
@ -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 ¶ms = 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;
|
||||
}
|
||||
|
@ -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") {
|
||||
|
@ -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)
|
||||
|
@ -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") {};
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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 ¶ms : ci->params)
|
||||
for (auto ¶ms : 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
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user