Compare commits
2 Commits
e6fa717507
...
8d2261e245
Author | SHA1 | Date |
---|---|---|
David Anderson | 8d2261e245 | |
David Anderson | db30e4a23f |
|
@ -0,0 +1,17 @@
|
||||||
|
package Top;
|
||||||
|
|
||||||
|
import ECP5_RAM::*;
|
||||||
|
|
||||||
|
(* synthesize *)
|
||||||
|
module mkTop(ECP5_EBRCorePort#(Bit#(12), Bit#(8)));
|
||||||
|
let clk <- exposeCurrentClock;
|
||||||
|
let rstN <- exposeCurrentReset;
|
||||||
|
|
||||||
|
ECP5_EBRPortConfig cfg = defaultValue;
|
||||||
|
ECP5_EBRCore#(Bit#(12), Bit#(8), UInt#(12), UInt#(8)) ram <- mkECP5_EBRCoreByte(cfg, cfg);
|
||||||
|
|
||||||
|
method put = ram.portA.put;
|
||||||
|
method read = ram.portA.read;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
endpackage
|
|
@ -0,0 +1,247 @@
|
||||||
|
package ECP5_RAM;
|
||||||
|
|
||||||
|
import Printf::*;
|
||||||
|
|
||||||
|
// ECP5_EBRWriteMode specifies what the EBR outputs on a write cycle.
|
||||||
|
typedef enum {
|
||||||
|
// In Normal mode, the EBR's output on a write cycle is undefined.
|
||||||
|
Normal,
|
||||||
|
// In WriteThrough mode, the EBR outputs the new value at the
|
||||||
|
// written address.
|
||||||
|
WriteThrough,
|
||||||
|
// In ReadBeforeWrite mode, the EBR outputs the prior value of the
|
||||||
|
// written address. ReadBeforeWrite is only available on 9 and 18
|
||||||
|
// bit ports.
|
||||||
|
ReadBeforeWrite
|
||||||
|
} ECP5_EBRWriteMode deriving (Bits, Eq);
|
||||||
|
|
||||||
|
// ECP5_EBRPortConfig is the static configuration of an EBR port.
|
||||||
|
typedef struct {
|
||||||
|
Clock clk;
|
||||||
|
Reset rstN;
|
||||||
|
// By default, ECP5 EBRs only register the input address and write
|
||||||
|
// data, giving a 1-cycle latency for operations. If
|
||||||
|
// registered_output is true, the output value is also registered,
|
||||||
|
// resulting in 2 cycles of latency but shorter datapaths.
|
||||||
|
Bool registered_output;
|
||||||
|
// chip_select_addr is the chip address of this EBR port. put
|
||||||
|
// method invocations whose select argument don't match this
|
||||||
|
// address are ignored.
|
||||||
|
UInt#(3) chip_select_addr;
|
||||||
|
// write_mode specifies the output's behavior for write operations.
|
||||||
|
ECP5_EBRWriteMode write_mode;
|
||||||
|
} ECP5_EBRPortConfig;
|
||||||
|
|
||||||
|
instance DefaultValue#(ECP5_EBRPortConfig);
|
||||||
|
defaultValue = ECP5_EBRPortConfig{
|
||||||
|
clk: noClock,
|
||||||
|
rstN: noReset,
|
||||||
|
registered_output: False,
|
||||||
|
chip_select_addr: 0,
|
||||||
|
write_mode: Normal
|
||||||
|
};
|
||||||
|
endinstance
|
||||||
|
|
||||||
|
(* always_ready *)
|
||||||
|
interface ECP5_EBRCoreInnerPort;
|
||||||
|
// Put starts a read or write operation, if select's value matches
|
||||||
|
// the port's configured chip_select_addr.
|
||||||
|
method Action put(UInt#(3) select, Bool write, Bit#(14) address, Bit#(18) data);
|
||||||
|
// Read returns the value on the EBR's output port. The output
|
||||||
|
// value is only defined when the read follows a put with the
|
||||||
|
// correct number of latency cycles for the port's configuration.
|
||||||
|
method Bit#(18) read();
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
interface ECP5_EBRCoreInner;
|
||||||
|
interface ECP5_EBRCoreInnerPort portA;
|
||||||
|
interface ECP5_EBRCoreInnerPort portB;
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
// mkECP5_EBRCoreInner instantiates an ECP5 EBR primitive with the
|
||||||
|
// given configuration. The returned interface has full-width I/O
|
||||||
|
// ports
|
||||||
|
import "BVI" ECP5_RAM =
|
||||||
|
module mkECP5_EBRCoreInner#(ECP5_EBRPortConfig port_a,
|
||||||
|
ECP5_EBRPortConfig port_b,
|
||||||
|
Integer portA_width,
|
||||||
|
Integer portB_width)
|
||||||
|
(ECP5_EBRCoreInner);
|
||||||
|
|
||||||
|
default_clock no_clock;
|
||||||
|
default_reset no_reset;
|
||||||
|
|
||||||
|
input_clock portA_clk(CLKA, (* unused *)CLKA_GATE) = port_a.clk;
|
||||||
|
input_reset portA_rstN(RSTA) clocked_by(portA_clk) = port_a.rstN;
|
||||||
|
|
||||||
|
input_clock portB_clk(CLKB, (* unused *)CLKB_GATE) = port_b.clk;
|
||||||
|
input_reset portB_rstN(RSTB) clocked_by(portB_clk) = port_b.rstN;
|
||||||
|
|
||||||
|
parameter DATA_WIDTH_A = portA_width;
|
||||||
|
parameter REGMODE_A = port_a.registered_output ? "OUTREG" : "NOREG";
|
||||||
|
parameter CSDECODE_A = "0b000"; //$format("0b%b", port_a.chip_select_addr);
|
||||||
|
parameter WRITEMODE_A = case (port_a.write_mode) matches
|
||||||
|
Normal: "NORMAL";
|
||||||
|
WriteThrough: "WRITETHROUGH";
|
||||||
|
ReadBeforeWrite: "READBEFOREWRITE";
|
||||||
|
endcase;
|
||||||
|
|
||||||
|
parameter DATA_WIDTH_B = portB_width;
|
||||||
|
parameter REGMODE_B = port_b.registered_output ? "OUTREG" : "NOREG";
|
||||||
|
parameter CSDECODE_B = "0b000"; //$format("0b%b", port_b.chip_select_addr);
|
||||||
|
parameter WRITEMODE_B = case (port_b.write_mode) matches
|
||||||
|
Normal: "NORMAL";
|
||||||
|
WriteThrough: "WRITETHROUGH";
|
||||||
|
ReadBeforeWrite: "READBEFOREWRITE";
|
||||||
|
endcase;
|
||||||
|
|
||||||
|
port OCEA = True;
|
||||||
|
port OCEB = True;
|
||||||
|
|
||||||
|
interface ECP5_EBRCoreInnerPort portA;
|
||||||
|
method put((*reg*)CSA, (*reg*)WEA, (*reg*)ADA, (*reg*)DIA) enable(CEA) clocked_by(portA_clk) reset_by(portA_rstN);
|
||||||
|
method DOA read() clocked_by(portA_clk) reset_by(portA_rstN);
|
||||||
|
endinterface
|
||||||
|
interface ECP5_EBRCoreInnerPort portB;
|
||||||
|
method put((*reg*)CSB, (*reg*)WEB, (*reg*)ADB, (*reg*)DIB) enable(CEB) clocked_by(portB_clk) reset_by(portB_rstN);
|
||||||
|
method DOB read() clocked_by(portB_clk) reset_by(portB_rstN);
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
schedule (portA.read) CF (portA.read, portA.put);
|
||||||
|
schedule (portA.put) C (portA.put);
|
||||||
|
schedule (portB.read) CF (portB.read, portB.put);
|
||||||
|
schedule (portB.put) C (portB.put);
|
||||||
|
endmodule : mkECP5_EBRCoreInner
|
||||||
|
|
||||||
|
module checkSizes#(addr a, data d, String module_name, String port_name)(Empty)
|
||||||
|
provisos (Bits#(addr, addr_sz),
|
||||||
|
Bits#(data, data_sz));
|
||||||
|
|
||||||
|
let data_sz = valueOf(data_sz);
|
||||||
|
let addr_sz = valueOf(addr_sz);
|
||||||
|
let addr_max = case (data_sz) matches
|
||||||
|
1: 14;
|
||||||
|
2: 13;
|
||||||
|
4: 12;
|
||||||
|
9: 11;
|
||||||
|
18: 10;
|
||||||
|
default: error(sprintf("invalid data width %d for port, must be one of 1,2,4,9,18", data_sz));
|
||||||
|
endcase;
|
||||||
|
if (addr_sz > addr_max) begin
|
||||||
|
addr dummy = ?;
|
||||||
|
errorM(sprintf("The address type for port %s of %s is wider than the hardware can implement. "+
|
||||||
|
"Address type %s has %d bits, maximum is %d",
|
||||||
|
port_name, module_name,
|
||||||
|
printType(typeOf(dummy)),
|
||||||
|
addr_sz,
|
||||||
|
addr_max));
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
// ECP5_EBRCorePort is the raw interface to one port of an ECP5 EBR
|
||||||
|
// memory block.
|
||||||
|
//
|
||||||
|
// The port has no implicit conditions, it is the caller's
|
||||||
|
// responsibility to wait the correct number of cycles after a put()
|
||||||
|
// before capturing data with read(). The caller must wait 1 cycle for
|
||||||
|
// unregistered ports, and 2 cycles for registered ports. When invoked
|
||||||
|
// at other times, read() returns an unspecified arbitrary value.
|
||||||
|
interface ECP5_EBRCorePort#(type addr, type data);
|
||||||
|
method Action put(UInt#(3) chip_select, Bool write, addr address, data datain);
|
||||||
|
method data read();
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
// ECP5_EBRCore is the raw interface to an ECP5 EBR memory block.
|
||||||
|
//
|
||||||
|
// The ports have no implicit conditions, the caller must wait the
|
||||||
|
// correct number of latency cycles to get valid data.
|
||||||
|
//
|
||||||
|
// It is the caller's responsibility to enforce synchronization
|
||||||
|
// between the ports, as specified in Lattice Technical Note 02204:
|
||||||
|
// the two ports must not issue concurrent writes to the same address,
|
||||||
|
// or a write concurrent with a read of the same address. If the two
|
||||||
|
// ports are being operated from different clock domains, the caller
|
||||||
|
// must implement appropriate synchronization to ensure that no
|
||||||
|
// read-during-write or write-during-write races occur.
|
||||||
|
interface ECP5_EBRCore#(type portA_addr, type portA_data, type portB_addr, type portB_data);
|
||||||
|
interface ECP5_EBRCorePort#(portA_addr, portA_data) portA;
|
||||||
|
interface ECP5_EBRCorePort#(portB_addr, portB_data) portB;
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
// mkECP5_EBRCore instantiates an ECP5 EBR memory primitive with the
|
||||||
|
// given configuration. This memory has no implicit or explicit
|
||||||
|
// conditions, the caller is responsible for upholding the primitive's
|
||||||
|
// timing and synchronization requirements.
|
||||||
|
module mkECP5_EBRCore#(ECP5_EBRPortConfig port_a,
|
||||||
|
ECP5_EBRPortConfig port_b)
|
||||||
|
(ECP5_EBRCore#(addr_a, data_a, addr_b, data_b))
|
||||||
|
provisos (Bits#(addr_a, addr_sz_a),
|
||||||
|
Bits#(data_a, data_sz_a),
|
||||||
|
Bits#(addr_b, addr_sz_b),
|
||||||
|
Bits#(data_b, data_sz_b),
|
||||||
|
Add#(addr_a_pad, addr_sz_a, 14),
|
||||||
|
Add#(data_a_pad, data_sz_a, 18),
|
||||||
|
Add#(addr_b_pad, addr_sz_b, 14),
|
||||||
|
Add#(data_b_pad, data_sz_b, 18));
|
||||||
|
|
||||||
|
checkSizes(addr_a ' (?), data_a ' (?), "mkECP5_EBRCore", "A");
|
||||||
|
checkSizes(addr_b ' (?), data_b ' (?), "mkECP5_EBRCore", "B");
|
||||||
|
|
||||||
|
let inner <- mkECP5_EBRCoreInner(port_a, port_b, valueOf(data_sz_a), valueOf(data_sz_b));
|
||||||
|
|
||||||
|
interface ECP5_EBRCorePort portA;
|
||||||
|
method Action put(UInt#(3) chip_select, Bool write, addr_a address, data_a datain);
|
||||||
|
inner.portA.put(chip_select, write, zeroExtend(pack(address)), zeroExtend(pack(datain)));
|
||||||
|
endmethod
|
||||||
|
method data_a read();
|
||||||
|
return unpack(truncate(inner.portA.read()));
|
||||||
|
endmethod
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
interface ECP5_EBRCorePort portB;
|
||||||
|
method Action put(UInt#(3) chip_select, Bool write, addr_b address, data_b datain);
|
||||||
|
inner.portB.put(chip_select, write, zeroExtend(pack(address)), zeroExtend(pack(datain)));
|
||||||
|
endmethod
|
||||||
|
method data_b read();
|
||||||
|
return unpack(truncate(inner.portB.read()));
|
||||||
|
endmethod
|
||||||
|
endinterface
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module mkECP5_EBRCoreByte#(ECP5_EBRPortConfig port_a,
|
||||||
|
ECP5_EBRPortConfig port_b)
|
||||||
|
(ECP5_EBRCore#(addr_a, data_a, addr_b, data_b))
|
||||||
|
provisos (Bits#(addr_a, 12),
|
||||||
|
Bits#(data_a, 8),
|
||||||
|
Bits#(addr_b, 12),
|
||||||
|
Bits#(data_b, 8));
|
||||||
|
|
||||||
|
let ebr1 <- mkECP5_EBRCore(port_a, port_b);
|
||||||
|
let ebr2 <- mkECP5_EBRCore(port_a, port_b);
|
||||||
|
|
||||||
|
interface ECP5_EBRCorePort portA;
|
||||||
|
method Action put(UInt#(3) chip_select, Bool write, addr_a address, data_a datain);
|
||||||
|
let data_bits = pack(datain);
|
||||||
|
ebr1.portA.put(chip_select, write, address, data_bits[7:4]);
|
||||||
|
ebr2.portA.put(chip_select, write, address, data_bits[3:0]);
|
||||||
|
endmethod
|
||||||
|
|
||||||
|
method data_a read();
|
||||||
|
return unpack({ebr1.portA.read(), ebr2.portA.read});
|
||||||
|
endmethod
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
interface ECP5_EBRCorePort portB;
|
||||||
|
method Action put(UInt#(3) chip_select, Bool write, addr_b address, data_b datain);
|
||||||
|
let data_bits = pack(datain);
|
||||||
|
ebr1.portB.put(chip_select, write, address, data_bits[7:4]);
|
||||||
|
ebr2.portB.put(chip_select, write, address, data_bits[3:0]);
|
||||||
|
endmethod
|
||||||
|
|
||||||
|
method data_b read();
|
||||||
|
return unpack({ebr1.portB.read(), ebr2.portB.read});
|
||||||
|
endmethod
|
||||||
|
endinterface
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
endpackage
|
|
@ -0,0 +1,182 @@
|
||||||
|
module ECP5_RAM(CLKA,
|
||||||
|
CEA,
|
||||||
|
OCEA,
|
||||||
|
WEA,
|
||||||
|
DIA,
|
||||||
|
ADA,
|
||||||
|
CSA,
|
||||||
|
RSTA,
|
||||||
|
DOA,
|
||||||
|
|
||||||
|
CLKB,
|
||||||
|
CEB,
|
||||||
|
OCEB,
|
||||||
|
WEB,
|
||||||
|
DIB,
|
||||||
|
ADB,
|
||||||
|
CSB,
|
||||||
|
RSTB,
|
||||||
|
DOB);
|
||||||
|
|
||||||
|
parameter GSR = "AUTO";
|
||||||
|
parameter RESETMODE = "SYNC";
|
||||||
|
parameter ASYNC_RESET_RELEASE = "SYNC";
|
||||||
|
|
||||||
|
parameter DATA_WIDTH_A = 18;
|
||||||
|
parameter REGMODE_A = "NOREG";
|
||||||
|
parameter WRITEMODE_A = "NORMAL";
|
||||||
|
parameter CSDECODE_A = "0b000";
|
||||||
|
|
||||||
|
parameter DATA_WIDTH_B = 18;
|
||||||
|
parameter REGMODE_B = "NOREG";
|
||||||
|
parameter WRITEMODE_B = "NORMAL";
|
||||||
|
parameter CSDECODE_B = "0b000";
|
||||||
|
|
||||||
|
input CLKA;
|
||||||
|
input CEA;
|
||||||
|
input OCEA;
|
||||||
|
input WEA;
|
||||||
|
input [17:0] DIA;
|
||||||
|
input [13:0] ADA;
|
||||||
|
input [2:0] CSA;
|
||||||
|
input RSTA;
|
||||||
|
output [17:0] DOA;
|
||||||
|
|
||||||
|
input CLKB;
|
||||||
|
input CEB;
|
||||||
|
input OCEB;
|
||||||
|
input WEB;
|
||||||
|
input [17:0] DIB;
|
||||||
|
input [13:0] ADB;
|
||||||
|
input [2:0] CSB;
|
||||||
|
input RSTB;
|
||||||
|
output [17:0] DOB;
|
||||||
|
|
||||||
|
DP16KD#(.GSR(GSR),
|
||||||
|
.RESETMODE(RESETMODE),
|
||||||
|
.ASYNC_RESET_RELEASE(ASYNC_RESET_RELEASE),
|
||||||
|
.DATA_WIDTH_A(DATA_WIDTH_A),
|
||||||
|
.REGMODE_A(REGMODE_A),
|
||||||
|
.WRITEMODE_A(WRITEMODE_A),
|
||||||
|
.CSDECODE_A(CSDECODE_A),
|
||||||
|
.DATA_WIDTH_B(DATA_WIDTH_B),
|
||||||
|
.REGMODE_B(REGMODE_B),
|
||||||
|
.WRITEMODE_B(WRITEMODE_B),
|
||||||
|
.CSDECODE_B(CSDECODE_B)) ram(.CLKA(CLKA),
|
||||||
|
.CEA(CEA),
|
||||||
|
.OCEA(OCEA),
|
||||||
|
.WEA(WEA),
|
||||||
|
.DIA17(DIA[17]),
|
||||||
|
.DIA16(DIA[16]),
|
||||||
|
.DIA15(DIA[15]),
|
||||||
|
.DIA14(DIA[14]),
|
||||||
|
.DIA13(DIA[13]),
|
||||||
|
.DIA12(DIA[12]),
|
||||||
|
.DIA11(DIA[11]),
|
||||||
|
.DIA10(DIA[10]),
|
||||||
|
.DIA9(DIA[9]),
|
||||||
|
.DIA8(DIA[8]),
|
||||||
|
.DIA7(DIA[7]),
|
||||||
|
.DIA6(DIA[6]),
|
||||||
|
.DIA5(DIA[5]),
|
||||||
|
.DIA4(DIA[4]),
|
||||||
|
.DIA3(DIA[3]),
|
||||||
|
.DIA2(DIA[2]),
|
||||||
|
.DIA1(DIA[1]),
|
||||||
|
.DIA0(DIA[0]),
|
||||||
|
.ADA13(ADA[13]),
|
||||||
|
.ADA12(ADA[12]),
|
||||||
|
.ADA11(ADA[11]),
|
||||||
|
.ADA10(ADA[10]),
|
||||||
|
.ADA9(ADA[9]),
|
||||||
|
.ADA8(ADA[8]),
|
||||||
|
.ADA7(ADA[7]),
|
||||||
|
.ADA6(ADA[6]),
|
||||||
|
.ADA5(ADA[5]),
|
||||||
|
.ADA4(ADA[4]),
|
||||||
|
.ADA3(ADA[3]),
|
||||||
|
.ADA2(ADA[2]),
|
||||||
|
.ADA1(ADA[1]),
|
||||||
|
.ADA0(ADA[0]),
|
||||||
|
.CSA2(CSA[2]),
|
||||||
|
.CSA1(CSA[1]),
|
||||||
|
.CSA0(CSA[0]),
|
||||||
|
.RSTA(RSTA),
|
||||||
|
.DOA17(DOA[17]),
|
||||||
|
.DOA16(DOA[16]),
|
||||||
|
.DOA15(DOA[15]),
|
||||||
|
.DOA14(DOA[14]),
|
||||||
|
.DOA13(DOA[13]),
|
||||||
|
.DOA12(DOA[12]),
|
||||||
|
.DOA11(DOA[11]),
|
||||||
|
.DOA10(DOA[10]),
|
||||||
|
.DOA9(DOA[9]),
|
||||||
|
.DOA8(DOA[8]),
|
||||||
|
.DOA7(DOA[7]),
|
||||||
|
.DOA6(DOA[6]),
|
||||||
|
.DOA5(DOA[5]),
|
||||||
|
.DOA4(DOA[4]),
|
||||||
|
.DOA3(DOA[3]),
|
||||||
|
.DOA2(DOA[2]),
|
||||||
|
.DOA1(DOA[1]),
|
||||||
|
.DOA0(DOA[0]),
|
||||||
|
|
||||||
|
.CLKB(CLKB),
|
||||||
|
.CEB(CEB),
|
||||||
|
.OCEB(OCEB),
|
||||||
|
.WEB(WEB),
|
||||||
|
.DIB17(DIB[17]),
|
||||||
|
.DIB16(DIB[16]),
|
||||||
|
.DIB15(DIB[15]),
|
||||||
|
.DIB14(DIB[14]),
|
||||||
|
.DIB13(DIB[13]),
|
||||||
|
.DIB12(DIB[12]),
|
||||||
|
.DIB11(DIB[11]),
|
||||||
|
.DIB10(DIB[10]),
|
||||||
|
.DIB9(DIB[9]),
|
||||||
|
.DIB8(DIB[8]),
|
||||||
|
.DIB7(DIB[7]),
|
||||||
|
.DIB6(DIB[6]),
|
||||||
|
.DIB5(DIB[5]),
|
||||||
|
.DIB4(DIB[4]),
|
||||||
|
.DIB3(DIB[3]),
|
||||||
|
.DIB2(DIB[2]),
|
||||||
|
.DIB1(DIB[1]),
|
||||||
|
.DIB0(DIB[0]),
|
||||||
|
.ADB13(ADB[13]),
|
||||||
|
.ADB12(ADB[12]),
|
||||||
|
.ADB11(ADB[11]),
|
||||||
|
.ADB10(ADB[10]),
|
||||||
|
.ADB9(ADB[9]),
|
||||||
|
.ADB8(ADB[8]),
|
||||||
|
.ADB7(ADB[7]),
|
||||||
|
.ADB6(ADB[6]),
|
||||||
|
.ADB5(ADB[5]),
|
||||||
|
.ADB4(ADB[4]),
|
||||||
|
.ADB3(ADB[3]),
|
||||||
|
.ADB2(ADB[2]),
|
||||||
|
.ADB1(ADB[1]),
|
||||||
|
.ADB0(ADB[0]),
|
||||||
|
.CSA2(CSA[2]),
|
||||||
|
.CSA1(CSA[1]),
|
||||||
|
.CSA0(CSA[0]),
|
||||||
|
.RSTB(RSTA),
|
||||||
|
.DOB17(DOB[17]),
|
||||||
|
.DOB16(DOB[16]),
|
||||||
|
.DOB15(DOB[15]),
|
||||||
|
.DOB14(DOB[14]),
|
||||||
|
.DOB13(DOB[13]),
|
||||||
|
.DOB12(DOB[12]),
|
||||||
|
.DOB11(DOB[11]),
|
||||||
|
.DOB10(DOB[10]),
|
||||||
|
.DOB9(DOB[9]),
|
||||||
|
.DOB8(DOB[8]),
|
||||||
|
.DOB7(DOB[7]),
|
||||||
|
.DOB6(DOB[6]),
|
||||||
|
.DOB5(DOB[5]),
|
||||||
|
.DOB4(DOB[4]),
|
||||||
|
.DOB3(DOB[3]),
|
||||||
|
.DOB2(DOB[2]),
|
||||||
|
.DOB1(DOB[1]),
|
||||||
|
.DOB0(DOB[0]));
|
||||||
|
endmodule
|
68
tasks.py
68
tasks.py
|
@ -74,49 +74,86 @@ def expand_test_target(target):
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Unknown target type {t}")
|
raise ValueError(f"Unknown target type {t}")
|
||||||
|
|
||||||
|
def phase(name):
|
||||||
|
print("")
|
||||||
|
print("*"*(len(name)+6))
|
||||||
|
print(f"** {name} **")
|
||||||
|
print("*"*(len(name)+6))
|
||||||
|
print("")
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def build(c, target="."):
|
def build(c, target="."):
|
||||||
|
phase("Compile Bluespec")
|
||||||
|
|
||||||
|
verilog_files = []
|
||||||
for target in expand_build_target(target):
|
for target in expand_build_target(target):
|
||||||
out_info, out_verilog, out_bsc = ensure_build_dirs(target, "info", "verilog", "bsc")
|
out_info, out_verilog, out_bsc = ensure_build_dirs(target, "info", "verilog", "bsc")
|
||||||
print(f"Building {target}")
|
print(f"Building {target}")
|
||||||
c.run(f"bsc -aggressive-conditions -check-assert -u -verilog -info-dir {out_info} -vdir {out_verilog} -bdir {out_bsc} -p {target.parent}:lib:%/Libraries -show-module-use -show-compiles {target}")
|
c.run(f"bsc -aggressive-conditions -check-assert -u -verilog -info-dir {out_info} -vdir {out_verilog} -bdir {out_bsc} -p {target.parent}:lib:%/Libraries -show-module-use -show-compiles {target}")
|
||||||
|
|
||||||
|
module_name = Path(f"mk{target.stem}")
|
||||||
|
verilog_files.append(out_verilog / module_name.with_suffix(".v"))
|
||||||
|
with open(out_verilog / module_name.with_suffix(".use")) as f:
|
||||||
|
verilog_files.extend(find_verilog_modules(c, f.read().splitlines()))
|
||||||
|
|
||||||
|
print("\nVerilog files for synthesis:")
|
||||||
|
for v in verilog_files:
|
||||||
|
print(" "+str(v))
|
||||||
|
|
||||||
|
return verilog_files
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def synth(c, target):
|
def synth(c, target):
|
||||||
target = resolve_synth_target(target)
|
target = resolve_synth_target(target)
|
||||||
|
|
||||||
pin_map = target.parent / "pin_map.lpf"
|
|
||||||
if not pin_map.is_file():
|
|
||||||
raise ArgumentError(f"Pin mapping {pin_map} not found")
|
|
||||||
|
|
||||||
module_name = Path(f"mk{target.stem}")
|
|
||||||
out_info, out_verilog, out_bsc, out_yosys, out_nextpnr = ensure_build_dirs(target, "info", "verilog", "bsc", "yosys", "nextpnr")
|
out_info, out_verilog, out_bsc, out_yosys, out_nextpnr = ensure_build_dirs(target, "info", "verilog", "bsc", "yosys", "nextpnr")
|
||||||
|
|
||||||
build(c, target)
|
module_name = Path(f"mk{target.stem}")
|
||||||
|
verilog_files = build(c, target)
|
||||||
verilog_files = [out_verilog / module_name.with_suffix(".v")]
|
|
||||||
with open(out_verilog / module_name.with_suffix(".use")) as f:
|
|
||||||
verilog_files.extend(find_verilog_modules(c, f.read().splitlines()))
|
|
||||||
|
|
||||||
|
phase("Logic synthesis")
|
||||||
yosys_script = out_yosys / "script.ys"
|
yosys_script = out_yosys / "script.ys"
|
||||||
yosys_json = out_yosys / module_name.with_suffix(".json")
|
yosys_json = out_yosys / module_name.with_suffix(".json")
|
||||||
yosys_report = out_yosys / module_name.with_suffix(".stats")
|
yosys_report = out_yosys / module_name.with_suffix(".stats")
|
||||||
yosys_log = out_yosys / module_name.with_suffix(".log")
|
yosys_log = out_yosys / module_name.with_suffix(".log")
|
||||||
|
yosys_preprocessed = out_yosys / module_name.with_suffix(".v")
|
||||||
|
yosys_compiled = out_yosys / f"{module_name.stem}_compiled.v"
|
||||||
with open(yosys_script, "w", encoding="UTF-8") as f:
|
with open(yosys_script, "w", encoding="UTF-8") as f:
|
||||||
f.write(dedent(f"""\
|
f.write(dedent(f"""\
|
||||||
read_verilog -sv -defer {' '.join(str(f) for f in verilog_files)}
|
read_verilog -sv -defer {' '.join(str(f) for f in verilog_files)}
|
||||||
hierarchy -top {module_name}
|
hierarchy -top {module_name}
|
||||||
synth_ecp5 -top {module_name} -json {yosys_json}
|
synth_ecp5 -top {module_name} -run :map_ram
|
||||||
|
write_verilog -sv {yosys_preprocessed}
|
||||||
|
synth_ecp5 -run map_ram: -json {yosys_json}
|
||||||
|
write_verilog -sv {yosys_compiled}
|
||||||
tee -o {yosys_report} stat
|
tee -o {yosys_report} stat
|
||||||
"""))
|
"""))
|
||||||
c.run(f"yosys -q -L {yosys_log} -T {yosys_script}")
|
c.run(f"yosys -q -L {yosys_log} -T {yosys_script}")
|
||||||
with open(yosys_report) as f:
|
with open(yosys_report) as f:
|
||||||
print(f.read())
|
ls = f.readlines()[3:]
|
||||||
|
print("".join(ls))
|
||||||
|
print(f" JSON : {yosys_json}")
|
||||||
|
print(f" Log : {yosys_log}")
|
||||||
|
print(f" Flat : {yosys_preprocessed}")
|
||||||
|
print(f"Compiled : {yosys_compiled}")
|
||||||
|
|
||||||
|
pin_map = target.parent / "pin_map.lpf"
|
||||||
|
if not pin_map.is_file():
|
||||||
|
print(f"WARNING: no pin map at {pin_map}, skipping place&route and bitstream generation")
|
||||||
|
return
|
||||||
|
|
||||||
|
phase("Place and route")
|
||||||
nextpnr_out = out_nextpnr / module_name.with_suffix(".pnr")
|
nextpnr_out = out_nextpnr / module_name.with_suffix(".pnr")
|
||||||
out = c.run(f"nextpnr-ecp5 --85k --detailed-timing-report --report {out_nextpnr / "timing.json"} --json {yosys_json} --lpf {pin_map} --package=CABGA381 --textcfg {nextpnr_out}", hide='stderr')
|
nextpnr_log = out_nextpnr / module_name.with_suffix(".log")
|
||||||
print_filtered_paragraphs(out.stderr, "Device utilization", "Critical path", common_prefix="Info: ")
|
nextpnr_timing = out_nextpnr / module_name.with_suffix(".log")
|
||||||
|
out = c.run(f"nextpnr-ecp5 --85k --detailed-timing-report -l {nextpnr_log} --report {nextpnr_timing} --json {yosys_json} --lpf {pin_map} --package=CABGA381 --textcfg {nextpnr_out}") #, hide='stderr')
|
||||||
|
|
||||||
|
print_filtered_paragraphs(out.stderr, "Device utilisation", "Critical path", "Max frequency", "Max delay", common_prefix="Info: ")
|
||||||
|
print(f" PNR : {nextpnr_out}")
|
||||||
|
print(f" Log : {nextpnr_log}")
|
||||||
|
print(f"Timing : {nextpnr_timing}")
|
||||||
|
|
||||||
|
phase("Bitstream generation")
|
||||||
bitstream = nextpnr_out.with_suffix(".bit")
|
bitstream = nextpnr_out.with_suffix(".bit")
|
||||||
c.run(f"ecppack {nextpnr_out} {bitstream}")
|
c.run(f"ecppack {nextpnr_out} {bitstream}")
|
||||||
print(f"Wrote bitstream to {bitstream}")
|
print(f"Wrote bitstream to {bitstream}")
|
||||||
|
@ -139,4 +176,5 @@ def test(c, target="."):
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def clean(c):
|
def clean(c):
|
||||||
shutil.rmtree("out")
|
if Path("out").is_dir():
|
||||||
|
shutil.rmtree("out")
|
||||||
|
|
Loading…
Reference in New Issue