systems/qf100: factor out common qf100 stuff, add testbench
parent
d29cc9a38d
commit
200b8d229e
|
@ -20,10 +20,12 @@ interface Lanai_BlockRAM#(numeric type n);
|
|||
interface Lanai_Memory memory;
|
||||
endinterface
|
||||
|
||||
module mkBlockMemory#(String filename) (Lanai_BlockRAM#(k)) provisos (Log#(k, n));
|
||||
module mkBlockMemory#(Maybe#(String) filename) (Lanai_BlockRAM#(k)) provisos (Log#(k, n));
|
||||
BRAM_Configure cfg = defaultValue;
|
||||
cfg.latency = 1;
|
||||
cfg.loadFormat = tagged Hex filename;
|
||||
if (filename matches tagged Valid .fn) begin
|
||||
cfg.loadFormat = tagged Hex fn;
|
||||
end
|
||||
cfg.outFIFODepth = 3;
|
||||
cfg.allowWriteResponseBypass = False;
|
||||
BRAM2PortBE#(Bit#(n), Bit#(32), 4) bram <- mkBRAM2ServerBE(cfg);
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
load("//build/bluespec:rules.bzl", "bluespec_library", "bluesim_test")
|
||||
load("//build/synthesis:rules.bzl", "rtl_bundle")
|
||||
|
||||
bluespec_library(
|
||||
name = "qf100",
|
||||
srcs = [
|
||||
"QF100.bsv",
|
||||
],
|
||||
deps = [
|
||||
"//lanai:cpu",
|
||||
"//lanai/frontend",
|
||||
"//lanai/frontend:spi_flash_controller",
|
||||
"//wishbone/peripherals:spi",
|
||||
"//wishbone/peripherals:gpio",
|
||||
],
|
||||
synthesize = {
|
||||
"QF100": [
|
||||
"mkQF100",
|
||||
"mkQF100Memory",
|
||||
"mkQF100SPI",
|
||||
"mkQF100GPIO",
|
||||
"mkQF100Fabric",
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
bluespec_library(
|
||||
name = "Tb",
|
||||
srcs = [
|
||||
"SPIFlashEmulator.bsv",
|
||||
"Tb.bsv",
|
||||
],
|
||||
data = [
|
||||
":flash.bin",
|
||||
],
|
||||
deps = [
|
||||
":qf100",
|
||||
],
|
||||
synthesize = {
|
||||
"Tb": [
|
||||
"mkTbQF100",
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
bluesim_test(
|
||||
name = "test_qf100",
|
||||
deps = [
|
||||
":Tb",
|
||||
],
|
||||
top = "mkTbQF100",
|
||||
)
|
||||
|
||||
|
||||
bluespec_library(
|
||||
name = "spi_flash_emulator",
|
||||
srcs = [
|
||||
"SPIFlashEmulator.bsv",
|
||||
],
|
||||
)
|
|
@ -0,0 +1,125 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2022 Sergiusz Bazanski
|
||||
|
||||
package QF100;
|
||||
|
||||
import GetPut :: *;
|
||||
import ClientServer :: *;
|
||||
import Connectable :: *;
|
||||
import Lanai_IFC :: *;
|
||||
import Lanai_CPU :: *;
|
||||
import Lanai_Memory :: *;
|
||||
import LanaiFrontend :: *;
|
||||
import SPIFlashController :: *;
|
||||
import RAM :: *;
|
||||
import WishboneCrossbar :: *;
|
||||
import WishboneSPI :: *;
|
||||
import WishboneGPIO :: *;
|
||||
|
||||
|
||||
Bit#(1) wbAddrSPI = 0;
|
||||
Bit#(1) wbAddrGPIO = 1;
|
||||
|
||||
function Maybe#(WishboneCrossbar::DecodedAddr#(2, 32)) decoder(Bit#(32) address);
|
||||
return case (address) matches
|
||||
32'h4001_30??: tagged Valid DecodedAddr { downstream: wbAddrSPI, address: address & 32'hff };
|
||||
32'h4001_08??: tagged Valid DecodedAddr { downstream: wbAddrGPIO, address: address & 32'hff };
|
||||
default: tagged Invalid;
|
||||
endcase;
|
||||
endfunction
|
||||
|
||||
(* synthesize *)
|
||||
module mkQF100Memory(Lanai_BlockRAM#(2048));
|
||||
Lanai_BlockRAM#(2048) inner <- mkBlockMemory(tagged Invalid);
|
||||
return inner;
|
||||
endmodule
|
||||
|
||||
(* synthesize *)
|
||||
module mkQF100SPI(WishboneSPI::SPIController#(32));
|
||||
let res <- mkSPIController;
|
||||
interface slave = res.slave;
|
||||
interface spiMaster = res.spiMaster;
|
||||
endmodule
|
||||
|
||||
(* synthesize *)
|
||||
module mkQF100GPIO(WishboneGPIO::GPIOController#(32));
|
||||
let res <- mkGPIOController;
|
||||
interface slave = res.slave;
|
||||
method in = res.in;
|
||||
method out = res.out;
|
||||
method oe = res.oe;
|
||||
endmodule
|
||||
|
||||
interface QF100Fabric;
|
||||
interface Wishbone::Slave#(32, 32, 4) cpu;
|
||||
interface Wishbone::Master#(32, 32, 4) spi;
|
||||
interface Wishbone::Master#(32, 32, 4) gpio;
|
||||
endinterface
|
||||
|
||||
(* synthesize *)
|
||||
module mkQF100Fabric(QF100Fabric);
|
||||
WishboneCrossbar::Crossbar#(1, 2, 32, 32, 4) fabric <- mkCrossbar(decoder);
|
||||
|
||||
interface cpu = fabric.upstreams[0];
|
||||
interface spi = fabric.downstreams[wbAddrSPI];
|
||||
interface gpio = fabric.downstreams[wbAddrGPIO];
|
||||
endmodule
|
||||
|
||||
interface QF100;
|
||||
// Memory SPI.
|
||||
interface WishboneSPI::Master mspi;
|
||||
(* always_ready *)
|
||||
method Bool mspi_csb;
|
||||
|
||||
// General purpose SPI.
|
||||
interface WishboneSPI::Master spi;
|
||||
|
||||
// GPIO.
|
||||
(* always_ready *)
|
||||
method Bit#(16) gpio_oe;
|
||||
(* always_ready *)
|
||||
method Bit#(16) gpio_out;
|
||||
(* always_ready, always_enabled *)
|
||||
method Action gpio_in(Bit#(16) value);
|
||||
endinterface
|
||||
|
||||
(* synthesize *)
|
||||
module mkQF100(QF100);
|
||||
LanaiFrontend frontend <- mkLanaiFrontend;
|
||||
|
||||
Lanai_IFC cpu <- mkLanaiCPU;
|
||||
mkConnection(cpu.imem_client, frontend.core_imem);
|
||||
mkConnection(cpu.dmem_client, frontend.core_dmem);
|
||||
|
||||
Lanai_BlockRAM#(2048) ram <- mkQF100Memory;
|
||||
mkConnection(frontend.ram_imem, ram.memory.imem);
|
||||
mkConnection(frontend.ram_dmem, ram.memory.dmem);
|
||||
|
||||
SPIFlashController#(16, 64) fmc <- mkSPIFlashController;
|
||||
mkConnection(frontend.fmc_imem, fmc.serverA);
|
||||
rule fmcDMemTranslate;
|
||||
let req <- frontend.fmc_dmem.request.get();
|
||||
fmc.serverB.request.put(req.addr);
|
||||
endrule
|
||||
mkConnection(frontend.fmc_dmem.response, fmc.serverB.response);
|
||||
|
||||
QF100Fabric fabric <- mkQF100Fabric;
|
||||
mkConnection(cpu.sysmem_client, fabric.cpu);
|
||||
|
||||
WishboneSPI::SPIController#(32) spiCtrl <- mkQF100SPI;
|
||||
mkConnection(fabric.spi, spiCtrl.slave);
|
||||
|
||||
WishboneGPIO::GPIOController#(32) gpioCtrl <- mkQF100GPIO;
|
||||
mkConnection(fabric.gpio, gpioCtrl.slave);
|
||||
|
||||
interface mspi = fmc.spi;
|
||||
method Bool mspi_csb;
|
||||
return unpack(fmc.csb);
|
||||
endmethod
|
||||
interface spi = spiCtrl.spiMaster;
|
||||
method gpio_oe = gpioCtrl.oe;
|
||||
method gpio_out = gpioCtrl.out;
|
||||
method gpio_in = gpioCtrl.in;
|
||||
endmodule
|
||||
|
||||
endpackage
|
|
@ -0,0 +1,191 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (C) 2022 Sergiusz Bazanski
|
||||
|
||||
package SPIFlashEmulator;
|
||||
|
||||
import BRAM :: *;
|
||||
import FIFO :: *;
|
||||
import SpecialFIFOs :: *;
|
||||
import StmtFSM :: *;
|
||||
|
||||
interface SPIFlashEmulator;
|
||||
(* always_enabled, always_ready *)
|
||||
method Action mosi(Bool value);
|
||||
(* always_enabled *)
|
||||
method Bool miso;
|
||||
(* always_enabled, always_ready *)
|
||||
method Action sclk(Bool value);
|
||||
(* always_enabled, always_ready *)
|
||||
method Action csb(Bool value);
|
||||
endinterface
|
||||
|
||||
module mkSPIFlashEmulator#(String filename) (SPIFlashEmulator);
|
||||
BRAM_Configure cfg = defaultValue;
|
||||
cfg.latency = 1;
|
||||
cfg.loadFormat = tagged Hex filename;
|
||||
cfg.outFIFODepth = 3;
|
||||
cfg.allowWriteResponseBypass = False;
|
||||
BRAM1Port#(Bit#(12), Bit#(32)) bram <- mkBRAM1Server(cfg);
|
||||
|
||||
Reg#(Bool) csbReg <- mkReg(False);
|
||||
Reg#(Bool) sclkReg <- mkReg(False);
|
||||
Reg#(Bool) mosiReg <- mkReg(False);
|
||||
|
||||
Reg#(Bit#(8)) incomingByte <- mkReg(0);
|
||||
Reg#(Maybe#(Bit#(3))) bitNo <- mkReg(tagged Invalid);
|
||||
|
||||
rule startReceive(bitNo matches tagged Invalid &&& csbReg == False);
|
||||
bitNo <= tagged Valid 0;
|
||||
endrule
|
||||
|
||||
Reg#(Bool) prevSclk <- mkReg(False);
|
||||
rule updatePrevSclk;
|
||||
prevSclk <= sclkReg;
|
||||
endrule
|
||||
|
||||
FIFO#(Maybe#(Bit#(8))) pendingByte <- mkPipelineFIFO;
|
||||
|
||||
Bool clockRisen = prevSclk == False && sclkReg == True;
|
||||
|
||||
rule onClockRisen(bitNo matches tagged Valid .bn &&& csbReg == False &&& clockRisen);
|
||||
let val = incomingByte;
|
||||
val[7-bn] = pack(mosiReg);
|
||||
|
||||
if (bn == 7) begin
|
||||
pendingByte.enq(tagged Valid val);
|
||||
bitNo <= tagged Invalid;
|
||||
end else begin
|
||||
incomingByte <= val;
|
||||
bitNo <= tagged Valid (bn + 1);
|
||||
end
|
||||
endrule
|
||||
|
||||
rule onCSBHigh(bitNo matches tagged Valid .bn &&& csbReg == True);
|
||||
pendingByte.enq(tagged Invalid);
|
||||
bitNo <= tagged Invalid;
|
||||
endrule
|
||||
|
||||
Reg#(Bit#(8)) command <- mkReg(0);
|
||||
Reg#(Bool) failed <- mkReg(False);
|
||||
Reg#(Bit#(24)) readAddr <- mkReg(0);
|
||||
Reg#(Bit#(24)) readAddrWait <- mkReg(0);
|
||||
Reg#(Bit#(32)) bramBuf <- mkReg(0);
|
||||
Reg#(Bit#(24)) bramAddr <- mkReg('h1);
|
||||
Reg#(Bit#(8)) sending <- mkReg(0);
|
||||
Stmt fsm = seq
|
||||
while (True) seq
|
||||
failed <= False;
|
||||
action
|
||||
pendingByte.deq();
|
||||
case (pendingByte.first) matches
|
||||
tagged Valid .v: command <= v;
|
||||
tagged Invalid: failed <= True;
|
||||
endcase
|
||||
endaction
|
||||
|
||||
if (failed) continue;
|
||||
|
||||
$display("Flash emulator: recv %x", command);
|
||||
if (command == 3) seq
|
||||
action
|
||||
pendingByte.deq();
|
||||
case (pendingByte.first) matches
|
||||
tagged Valid .v: readAddr[23:16] <= v;
|
||||
tagged Invalid: failed <= True;
|
||||
endcase
|
||||
endaction
|
||||
if (failed) continue;
|
||||
action
|
||||
pendingByte.deq();
|
||||
case (pendingByte.first) matches
|
||||
tagged Valid .v: readAddr[15:8] <= v;
|
||||
tagged Invalid: failed <= True;
|
||||
endcase
|
||||
endaction
|
||||
if (failed) continue;
|
||||
action
|
||||
pendingByte.deq();
|
||||
case (pendingByte.first) matches
|
||||
tagged Valid .v: readAddr[7:0] <= v;
|
||||
tagged Invalid: failed <= True;
|
||||
endcase
|
||||
endaction
|
||||
if (failed) continue;
|
||||
|
||||
par
|
||||
while (!failed) seq
|
||||
while (readAddr[1:0] != 0 && failed == False) seq
|
||||
noAction;
|
||||
endseq
|
||||
readAddrWait <= readAddr;
|
||||
bram.portA.request.put(BRAMRequest { responseOnWrite: True
|
||||
, address: readAddrWait[13:2]
|
||||
, datain: 0
|
||||
, write: False
|
||||
});
|
||||
action
|
||||
let bramRes <- bram.portA.response.get();
|
||||
action
|
||||
bramBuf <= bramRes;
|
||||
bramAddr <= readAddrWait;
|
||||
endaction
|
||||
endaction
|
||||
endseq
|
||||
|
||||
seq
|
||||
while (bramAddr != readAddr) seq
|
||||
noAction;
|
||||
endseq
|
||||
action
|
||||
let byteInBuf = (7 - (readAddr & 'b111)) << 3;
|
||||
sending <= bramBuf[byteInBuf+7:byteInBuf];
|
||||
endaction
|
||||
$display("Flash emulator: read addr: %x", readAddr);
|
||||
while (!failed) seq
|
||||
action
|
||||
case (pendingByte.first) matches
|
||||
tagged Invalid: failed <= True;
|
||||
endcase
|
||||
pendingByte.deq();
|
||||
endaction
|
||||
seq
|
||||
readAddr <= readAddr + 1;
|
||||
while (bramAddr != (readAddr & (~'b11))) seq
|
||||
noAction;
|
||||
endseq
|
||||
action
|
||||
let byteInBuf = (7 - (readAddr & 'b111)) << 3;
|
||||
sending <= bramBuf[byteInBuf+7:byteInBuf];
|
||||
endaction
|
||||
endseq
|
||||
endseq
|
||||
$display("Flash emulator: read done");
|
||||
endseq
|
||||
endpar
|
||||
|
||||
endseq else seq
|
||||
$display("Flash emulator: unhandled command %x", command);
|
||||
endseq
|
||||
endseq
|
||||
endseq;
|
||||
|
||||
mkAutoFSM(fsm);
|
||||
|
||||
method Action mosi(Bool value);
|
||||
mosiReg <= value;
|
||||
endmethod
|
||||
method Action sclk(Bool value);
|
||||
sclkReg <= value;
|
||||
endmethod
|
||||
method Action csb(Bool value);
|
||||
csbReg <= value;
|
||||
endmethod
|
||||
method Bool miso;
|
||||
return case (bitNo) matches
|
||||
tagged Valid .bn: unpack(sending[7-bn]);
|
||||
tagged Invalid: False;
|
||||
endcase;
|
||||
endmethod
|
||||
endmodule
|
||||
|
||||
endpackage
|
|
@ -0,0 +1,34 @@
|
|||
package Tb;
|
||||
|
||||
import QF100 :: *;
|
||||
import SPIFlashEmulator :: *;
|
||||
import StmtFSM :: *;
|
||||
import WishboneSPI :: *;
|
||||
|
||||
(* synthesize *)
|
||||
module mkTbQF100(Empty);
|
||||
QF100 qf100 <- mkQF100;
|
||||
SPIFlashEmulator emu <- mkSPIFlashEmulator("systems/qf100/flash.bin");
|
||||
|
||||
rule feed_qf100_in;
|
||||
qf100.gpio_in(0);
|
||||
qf100.spi.miso(False);
|
||||
qf100.mspi.miso(emu.miso);
|
||||
endrule
|
||||
|
||||
rule feed_emu_in;
|
||||
emu.mosi(unpack(qf100.mspi.mosi));
|
||||
emu.sclk(unpack(qf100.mspi.sclk));
|
||||
emu.csb(qf100.mspi_csb);
|
||||
endrule
|
||||
|
||||
Reg#(Bit#(32)) tmp <- mkReg(0);
|
||||
Stmt test = seq
|
||||
for (tmp <= 0; tmp <= 20000; tmp <= tmp + 1) seq
|
||||
noAction;
|
||||
endseq
|
||||
endseq;
|
||||
mkAutoFSM(test);
|
||||
endmodule
|
||||
|
||||
endpackage
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue