commit
fe38e70dc1
@ -514,7 +514,7 @@ struct Arch : BaseArch<ArchRanges>
|
||||
return IdStringList(ids);
|
||||
}
|
||||
|
||||
DelayQuad getWireDelay(WireId wire) const override { return DelayQuad(0); }
|
||||
DelayQuad getWireDelay(WireId wire) const override { return DelayQuad(0.01); }
|
||||
|
||||
WireRange getWires() const override
|
||||
{
|
||||
@ -587,7 +587,7 @@ struct Arch : BaseArch<ArchRanges>
|
||||
return wire;
|
||||
}
|
||||
|
||||
DelayQuad getPipDelay(PipId pip) const override { return DelayQuad(0); }
|
||||
DelayQuad getPipDelay(PipId pip) const override { return DelayQuad(0.01); }
|
||||
|
||||
PipRange getPipsDownhill(WireId wire) const override
|
||||
{
|
||||
|
@ -71,9 +71,24 @@ static std::string get_trellis_wirename(Context *ctx, Location loc, WireId wire)
|
||||
name.find("JINCK") != std::string::npos);
|
||||
};
|
||||
|
||||
if (prefix2 == "G_" || prefix2 == "L_" || prefix2 == "R_" || prefix2 == "U_" || prefix2 == "D_" ||
|
||||
prefix7 == "BRANCH_")
|
||||
if (prefix2 == "G_" || prefix2 == "L_" || prefix2 == "R_" || prefix7 == "BRANCH_")
|
||||
return basename;
|
||||
|
||||
if (prefix2 == "U_" || prefix2 == "D_") {
|
||||
// We needded to keep U_ and D_ prefixes to generate the routing
|
||||
// graph connections properly, but in truth they are not relevant
|
||||
// outside of the center row of tiles as far as the database is
|
||||
// concerned. So convert U_/D_ prefixes back to G_ if not in the
|
||||
// center row.
|
||||
|
||||
// FIXME: This is hardcoded to 1200HC coordinates for now. Perhaps
|
||||
// add a center row/col field to chipdb?
|
||||
if (loc.y == 6)
|
||||
return basename;
|
||||
else
|
||||
return "G_" + basename.substr(2);
|
||||
}
|
||||
|
||||
if (loc == wire.location) {
|
||||
// TODO: JINCK is not currently handled by this.
|
||||
if (is_pio_wire(basename)) {
|
||||
@ -100,9 +115,21 @@ static std::string get_trellis_wirename(Context *ctx, Location loc, WireId wire)
|
||||
static void set_pip(Context *ctx, ChipConfig &cc, PipId pip)
|
||||
{
|
||||
std::string tile = ctx->get_pip_tilename(pip);
|
||||
std::string tile_type = ctx->chip_info->tiletype_names[ctx->tile_info(pip)->pips_data[pip.index].tile_type].get();
|
||||
std::string source = get_trellis_wirename(ctx, pip.location, ctx->getPipSrcWire(pip));
|
||||
std::string sink = get_trellis_wirename(ctx, pip.location, ctx->getPipDstWire(pip));
|
||||
cc.tiles[tile].add_arc(sink, source);
|
||||
|
||||
// Special case pips whose config bits are spread across tiles.
|
||||
if (source == "G_PCLKCIBVIQT0" && sink == "G_VPRXCLKI0") {
|
||||
if (tile_type == "CENTER7") {
|
||||
cc.tiles[ctx->get_tile_by_type("CENTER8")].add_arc(sink, source);
|
||||
} else if (tile_type == "CENTER8") {
|
||||
cc.tiles[ctx->get_tile_by_type("CENTER7")].add_arc(sink, source);
|
||||
} else {
|
||||
NPNR_ASSERT_FALSE("Tile does not contain special-cased pip");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static std::vector<bool> int_to_bitvector(int val, int size)
|
||||
|
@ -154,7 +154,7 @@ void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff)
|
||||
replace_port(lut, ctx->id("Z"), lc, ctx->id("F0"));
|
||||
}
|
||||
|
||||
void dff_to_lc(Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_lut)
|
||||
void dff_to_lc(Context *ctx, CellInfo *dff, CellInfo *lc, LutType lut_type)
|
||||
{
|
||||
// FIXME: This will have to change once we support FFs with reset value of 1.
|
||||
lc->params[ctx->id("REG0_REGSET")] = std::string("RESET");
|
||||
@ -163,14 +163,21 @@ void dff_to_lc(Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_lut)
|
||||
replace_port(dff, ctx->id("LSR"), lc, ctx->id("LSR"));
|
||||
replace_port(dff, ctx->id("Q"), lc, ctx->id("Q0"));
|
||||
|
||||
// If a register's DI port is fed by a constant, options for placing are
|
||||
// limited. Use the LUT to get around this.
|
||||
if (pass_thru_lut) {
|
||||
if (lut_type == LutType::PassThru) {
|
||||
// If a register's DI port is fed by a constant, options for placing are
|
||||
// limited. Use the LUT to get around this.
|
||||
// LUT output will go to F0, which will feed back to DI0 input.
|
||||
lc->params[ctx->id("LUT0_INITVAL")] = Property(0xAAAA, 16);
|
||||
;
|
||||
replace_port(dff, ctx->id("DI"), lc, ctx->id("A0"));
|
||||
connect_ports(ctx, lc, ctx->id("F0"), lc, ctx->id("DI0"));
|
||||
} else if (lut_type == LutType::None) {
|
||||
// If there is no LUT, use the M0 input because DI0 requires
|
||||
// going through the LUTs.
|
||||
lc->params[ctx->id("REG0_SD")] = std::string("0");
|
||||
replace_port(dff, ctx->id("DI"), lc, ctx->id("M0"));
|
||||
} else {
|
||||
// Otherwise, there's a LUT being used in the slice and mapping DI to
|
||||
// DI0 input is fine.
|
||||
replace_port(dff, ctx->id("DI"), lc, ctx->id("DI0"));
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,16 @@
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
// When packing DFFs, we need context of how it's connected to a LUT to
|
||||
// properly map DFF ports to FACADE_SLICEs; DI0 input muxes F0 and OFX0,
|
||||
// and a DFF inside a slice can use either DI0 or M0 as an input.
|
||||
enum class LutType
|
||||
{
|
||||
None,
|
||||
Normal,
|
||||
PassThru,
|
||||
};
|
||||
|
||||
// Create a MachXO2 arch cell and return it
|
||||
// Name will be automatically assigned if not specified
|
||||
std::unique_ptr<CellInfo> create_machxo2_cell(Context *ctx, IdString type, std::string name = "");
|
||||
@ -46,7 +56,7 @@ void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff = tr
|
||||
// and reconnecting signals as necessary. If pass_thru_lut is True, the LUT will
|
||||
// be configured as pass through and D connected to I0, otherwise D will be
|
||||
// ignored
|
||||
void dff_to_lc(Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_lut = false);
|
||||
void dff_to_lc(Context *ctx, CellInfo *dff, CellInfo *lc, LutType lut_type = LutType::Normal);
|
||||
|
||||
// Convert a nextpnr IO buffer to a GENERIC_IOB
|
||||
void nxio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *sbio, pool<IdString> &todelete_cells);
|
||||
|
@ -20,6 +20,7 @@ This directory contains a simple example of running `nextpnr-machxo2`:
|
||||
* `demo.sh` creates bitstreams for [TinyFPGA Ax](https://tinyfpga.com/a-series-guide.html)
|
||||
and writes the resulting bitstream to MachXO2's internal flash using
|
||||
[`tinyproga`](https://github.com/tinyfpga/TinyFPGA-A-Programmer).
|
||||
`demo-vhdl.sh` does the same, except using the [GHDL Yosys Plugin](https://github.com/ghdl/ghdl-yosys-plugin).
|
||||
|
||||
As `nextpnr-machxo2` is developed the contents `simple.sh`, `simtest.sh`,
|
||||
`mitertest.sh`, and `demo.sh` are subject to change.
|
||||
|
24
machxo2/examples/demo-vhdl.sh
Normal file
24
machxo2/examples/demo-vhdl.sh
Normal file
@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "Usage: $0 prefix"
|
||||
exit -1
|
||||
fi
|
||||
|
||||
if ! grep -q "LOC" $1.vhd; then
|
||||
echo "$1.vhd does not have LOC constraints for tinyfpga_a."
|
||||
exit -2
|
||||
fi
|
||||
|
||||
if [ ! -z ${TRELLIS_DB+x} ]; then
|
||||
DB_ARG="--db $TRELLIS_DB"
|
||||
fi
|
||||
|
||||
set -ex
|
||||
|
||||
${YOSYS:-yosys} -p "ghdl --std=08 prims.vhd ${1}.vhd -e;
|
||||
attrmap -tocase LOC
|
||||
synth_machxo2 -json ${1}-vhdl.json"
|
||||
${NEXTPNR:-../../nextpnr-machxo2} --1200 --package QFN32 --no-iobs --json $1-vhdl.json --textcfg $1-vhdl.txt
|
||||
ecppack --compress $DB_ARG $1-vhdl.txt $1-vhdl.bit
|
||||
tinyproga -b $1-vhdl.bit
|
18
machxo2/examples/prims.vhd
Normal file
18
machxo2/examples/prims.vhd
Normal file
@ -0,0 +1,18 @@
|
||||
library ieee;
|
||||
use ieee.std_logic_1164.all;
|
||||
|
||||
-- We don't have VHDL primitives yet, so declare them in examples for now.
|
||||
package components is
|
||||
|
||||
component OSCH
|
||||
generic (
|
||||
NOM_FREQ : string := "2.08"
|
||||
);
|
||||
port(
|
||||
STDBY : in std_logic;
|
||||
OSC : out std_logic;
|
||||
SEDSTDBY : out std_logic
|
||||
);
|
||||
end component;
|
||||
|
||||
end components;
|
38
machxo2/examples/tinyfpga.vhd
Normal file
38
machxo2/examples/tinyfpga.vhd
Normal file
@ -0,0 +1,38 @@
|
||||
library ieee ;
|
||||
context ieee.ieee_std_context;
|
||||
|
||||
use work.components.all;
|
||||
|
||||
entity top is
|
||||
port (
|
||||
pin1: out std_logic
|
||||
);
|
||||
|
||||
attribute LOC: string;
|
||||
attribute LOC of pin1: signal is "13";
|
||||
end;
|
||||
|
||||
architecture arch of top is
|
||||
signal clk: std_logic;
|
||||
signal led_timer: unsigned(23 downto 0) := (others=>'0');
|
||||
begin
|
||||
|
||||
internal_oscillator_inst: OSCH
|
||||
generic map (
|
||||
NOM_FREQ => "16.63"
|
||||
)
|
||||
port map (
|
||||
STDBY => '0',
|
||||
OSC => clk
|
||||
);
|
||||
|
||||
process(clk)
|
||||
begin
|
||||
if rising_edge(clk) then
|
||||
led_timer <= led_timer + 1;
|
||||
end if;
|
||||
end process;
|
||||
|
||||
pin1 <= led_timer(led_timer'left);
|
||||
|
||||
end;
|
@ -62,7 +62,7 @@ static void pack_lut_lutffs(Context *ctx)
|
||||
// Locations don't match, can't pack
|
||||
} else {
|
||||
lut_to_lc(ctx, ci, packed.get(), false);
|
||||
dff_to_lc(ctx, dff, packed.get(), false);
|
||||
dff_to_lc(ctx, dff, packed.get(), LutType::Normal);
|
||||
if (dff_bel != dff->attrs.end())
|
||||
packed->attrs[ctx->id("BEL")] = dff_bel->second;
|
||||
packed_cells.insert(dff->name);
|
||||
@ -105,7 +105,9 @@ static void pack_remaining_ffs(Context *ctx)
|
||||
packed->attrs[attr.first] = attr.second;
|
||||
|
||||
auto dff_bel = ci->attrs.find(ctx->id("BEL"));
|
||||
dff_to_lc(ctx, ci, packed.get(), false);
|
||||
|
||||
dff_to_lc(ctx, ci, packed.get(), LutType::None);
|
||||
|
||||
if (dff_bel != ci->attrs.end())
|
||||
packed->attrs[ctx->id("BEL")] = dff_bel->second;
|
||||
packed_cells.insert(ci->name);
|
||||
@ -146,7 +148,7 @@ static void set_net_constant(Context *ctx, NetInfo *orig, NetInfo *constnet, boo
|
||||
for (auto &attr : uc->attrs)
|
||||
lc->attrs[attr.first] = attr.second;
|
||||
|
||||
dff_to_lc(ctx, uc, lc.get(), true);
|
||||
dff_to_lc(ctx, uc, lc.get(), LutType::PassThru);
|
||||
packed_cells.insert(uc->name);
|
||||
|
||||
lc->ports[id_A0].net = constnet;
|
||||
|
Loading…
Reference in New Issue
Block a user