package VRAMCore; import Connectable::*; import GetPut::*; import ClientServer::*; import DReg::*; import BRAM::*; import Vector::*; import FIFOF::*; import SpecialFIFOs::*; import Real::*; import Printf::*; import DelayLine::*; import ECP5_RAM::*; export VRAMAddr; export VRAMData; export VRAMRequest(..); export VRAMResponse(..); export VRAMClient(..); export VRAMServer(..); export VRAMCore(..); export mkVRAMCore; typedef Bit#(8) VRAMData; // Each byte RAM we build below can address 4096 bytes, which is 12 // address bits. typedef UInt#(12) ByteAddr; typedef UInt#(3) ChipAddr; // ByteRAM is two EBRs glued together to make a whole-byte memory. typedef EBR#(ByteAddr, VRAMData, ByteAddr, VRAMData) ByteRAM; // mkByteRAM glues two ECP5 EBRs together to make a 4096x8b memory // block. Like the underlying ECP5 EBRs, callers must bring their own // flow control to read out responses one cycle after putting a read // request. module mkByteRAM(ChipAddr chip_addr, ByteRAM ifc); EBRPortConfig cfg = defaultValue; cfg.chip_select_addr = chip_addr; EBR#(ByteAddr, Bit#(4), ByteAddr, Bit#(4)) upper <- mkEBRCore(cfg, cfg); EBR#(ByteAddr, Bit#(4), ByteAddr, Bit#(4)) lower <- mkEBRCore(cfg, cfg); interface EBRPort portA; method Action put(ChipAddr chip_select, Bool write, ByteAddr addr, VRAMData data_in); upper.portA.put(chip_select, write, addr, truncate(data_in>>4)); lower.portA.put(chip_select, write, addr, truncate(data_in)); endmethod method VRAMData read(); return (extend(upper.portA.read())<<4) | (extend(lower.portA.read())); endmethod endinterface interface EBRPort portB; method Action put(ChipAddr chip_select, Bool write, ByteAddr addr, VRAMData data_in); upper.portB.put(chip_select, write, addr, truncate(data_in>>4)); lower.portB.put(chip_select, write, addr, truncate(data_in)); endmethod method VRAMData read(); return (extend(upper.portB.read())<<4) | (extend(lower.portB.read())); endmethod endinterface endmodule : mkByteRAM // mkByteRAMArray arrays up to 8 mkByteRAMs together, using the // hardwired chip select lines to route inputs appropriately and a mux // tree to collect outputs. With num_chips=8, the resulting ByteRAM is // 32768x8b. module mkByteRAMArray(Integer num_chips, ByteRAM ifc); if (num_chips > 8) error("mkByteRAMArray can only array 8 raw ByteRAMs"); ByteRAM blocks[num_chips]; for (Integer i=0; i 128) error("maximum VRAMCore size is 128KiB"); let num_bytes = num_kilobytes*1024; if (num_bytes % 4096 != 0) error("VRAMCore must be a multiple of 4096b"); let num_byterams = num_bytes/4096; let num_arrays = ceil(fromInteger(num_byterams) / 8); function Tuple3#(ArrayAddr, ChipAddr, ByteAddr) split_addr(VRAMAddr a); if (num_bytes < 128*1024) a = a % fromInteger(num_bytes); match {.top, .byteaddr} = split(pack(a)); Tuple2#(Bit#(SizeOf#(ArrayAddr)), Bit#(SizeOf#(ChipAddr))) route = split(top); return tuple3(unpack(tpl_1(route)), unpack(tpl_2(route)), unpack(byteaddr)); endfunction ByteRAM arrays[num_arrays]; for (Integer i=0; i