package VRAMCore; import GetPut::*; import ClientServer::*; import BRAMCore::*; import Real::*; import DelayLine::*; import ECP5_RAM::*; export VRAMAddr; export VRAMData; export VRAMRequest(..); export VRAMResponse(..); export VRAMCore(..); export mkVRAMCore; typedef Bit#(8) VRAMData; typedef UInt#(17) VRAMAddr; typedef UInt#(2) ArrayAddr; typedef UInt#(3) ChipAddr; typedef UInt#(12) ByteAddr; // ByteRAM is two EBRs glued together to make a whole-byte memory. typedef EBR#(ByteAddr, VRAMData, ByteAddr, VRAMData) ByteRAM; typedef struct { VRAMAddr addr; Maybe#(VRAMData) data; } VRAMRequest deriving (Bits, Eq, FShow); typedef struct { VRAMData data; } VRAMResponse deriving (Bits, Eq, FShow); module mkNibbleRAM_ECP5(ChipAddr chip_addr, EBR#(ByteAddr, Bit#(4), ByteAddr, Bit#(4)) ifc); EBRPortConfig cfg = defaultValue; cfg.chip_select_addr = chip_addr; let _ret <- mkEBRCore(cfg, cfg); return _ret; endmodule module mkNibbleRAM_Sim(ChipAddr chip_addr, EBR#(ByteAddr, Bit#(4), ByteAddr, Bit#(4)) ifc); BRAM_DUAL_PORT#(ByteAddr, Bit#(4)) ram <- mkBRAMCore2(4096, False); interface EBRPort portA; method Action put(UInt#(3) chip_select, Bool write, ByteAddr address, Bit#(4) datain); if (chip_select == chip_addr) ram.a.put(write, address, datain); endmethod method read = ram.a.read; endinterface interface EBRPort portB; method Action put(UInt#(3) chip_select, Bool write, ByteAddr address, Bit#(4) datain); if (chip_select == chip_addr) ram.b.put(write, address, datain); endmethod method read = ram.b.read; endinterface endmodule module mkNibbleRAM(ChipAddr chip_addr, EBR#(ByteAddr, Bit#(4), ByteAddr, Bit#(4)) ifc); let _ret; if (genC()) _ret <- mkNibbleRAM_Sim(chip_addr); else _ret <- mkNibbleRAM_ECP5(chip_addr); return _ret; endmodule // 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); EBR#(ByteAddr, Bit#(4), ByteAddr, Bit#(4)) upper <- mkNibbleRAM(chip_addr); EBR#(ByteAddr, Bit#(4), ByteAddr, Bit#(4)) lower <- mkNibbleRAM(chip_addr); 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); return unpack(pack(a)); endfunction ByteRAM arrays[num_arrays]; for (Integer i=0; i