Compare commits

..

No commits in common. "1929bbe3cc0d66e2585d7c71fe357acb2d5eec7e" and "913c407224c59e60e05907b92b30eefcf9f18275" have entirely different histories.

4 changed files with 117 additions and 225 deletions

View File

@ -5,6 +5,7 @@ import Vector::*;
export MemArbiterOp(..); export MemArbiterOp(..);
export MemArbiterServer(..); export MemArbiterServer(..);
export MemArbiterClient(..);
export MemArbiter(..), mkPriorityMemArbiter, mkRoundRobinMemArbiter; export MemArbiter(..), mkPriorityMemArbiter, mkRoundRobinMemArbiter;
// A MemArbiterOp is an operation that a client is seeking permission // A MemArbiterOp is an operation that a client is seeking permission
@ -14,8 +15,6 @@ typedef struct {
addr addr; addr addr;
} MemArbiterOp#(type addr) deriving (Bits, Eq, FShow); } MemArbiterOp#(type addr) deriving (Bits, Eq, FShow);
// mem_ops_conflict reports whether memory accesses a and b would
// cause undefined behavior if they proceed simultaneously.
function Bool mem_ops_conflict(Maybe#(MemArbiterOp#(addr)) a, Maybe#(MemArbiterOp#(addr)) b) function Bool mem_ops_conflict(Maybe#(MemArbiterOp#(addr)) a, Maybe#(MemArbiterOp#(addr)) b)
provisos(Eq#(addr)); provisos(Eq#(addr));
@ -32,39 +31,58 @@ interface MemArbiterServer#(type addr);
method Bool grant(); method Bool grant();
endinterface endinterface
// A MemArbiterClient emits requests and receives grants.
interface MemArbiterClient#(type addr);
method Maybe#(MemArbiterOp#(addr)) request();
method Action grant();
endinterface
// Arbiter clients and servers can be connected in the obvious way.
instance Connectable#(MemArbiterClient#(addr), MemArbiterServer#(addr));
module mkConnection(MemArbiterClient#(addr) client, MemArbiterServer#(addr) server, Empty ifc);
rule send_request (client.request matches tagged Valid .req);
server.request(req);
endrule
rule send_grant (server.grant());
client.grant();
endrule
endmodule
endinstance
// A MemArbiter manages concurrent access to a memory port. // A MemArbiter manages concurrent access to a memory port.
interface MemArbiter#(numeric type num_clients, type addr); interface MemArbiter#(numeric type num_clients, type addr);
// ports allow clients to request memory access. // ports allow clients to request memory access.
interface Vector#(num_clients, MemArbiterServer#(addr)) ports; interface Vector#(num_clients, MemArbiterServer#(addr)) ports;
// granted_port returns the index in ports of the client that is
// being granted its request.
method UInt#(TLog#(num_clients)) granted_port();
// The following methods are to support arbiter chaining. // The following methods are to support arbiter chaining.
// //
// Suppose you're arbitrating access to a dual-port memory. // Suppose you're arbitrating access to a dual-port
// Typically, such a memory specifies that if one port is writing // memory. Typically, such a memory specifies that if one port is
// to an address, the other must not concurrently read or write // writing to an address, the other must not concurrently read or
// that same address. This means the arbiters attached to each // write that same address. This means the arbiters attached to
// memory port must cooperate to avoid simultaneously granting // each memory port must cooperate to avoid simultaneously granting
// conflicting requests from their clients. // conflicting requests from their clients.
// //
// conflict_in supplies an already granted operation that this // Calling conflict prevents the arbiter from granting a concurrent
// arbiter must avoid conflicting with. conflict_out emits the // request that would result in a write-write, read-write or
// operation that the arbiter is granting, if any. // write-read conflict. granted_op emits the operation that the
// arbiter is granting, if any.
// //
// mkConnection(firstArbiter, secondArbiter) gives conflict // MemArbiter intances are Connectable: mkConnection(a, b) gives
// priority to firstArbiter. That is, secondArbiter only grants // conflict priority to a. That is, b only grants requests that
// requests that don't conflict with grants made by firstArbiter. // don't conflict with a's grant.
(* always_ready *) (* always_ready *)
method Action conflict_in(MemArbiterOp#(addr) conflict); method Action conflict(MemArbiterOp#(addr) conflict);
method MemArbiterOp#(addr) conflict_out(); method MemArbiterOp#(addr) granted_op();
endinterface endinterface
instance Connectable#(MemArbiter#(m, addr), MemArbiter#(n, addr)); instance Connectable#(MemArbiter#(m, addr), MemArbiter#(n, addr));
module mkConnection(MemArbiter#(m, addr) a, MemArbiter#(n, addr) b, Empty ifc); module mkConnection(MemArbiter#(m, addr) a, MemArbiter#(n, addr) b, Empty ifc);
mkConnection(a.conflict_out, b.conflict_in); (* fire_when_enabled *)
rule forward_conflict;
b.conflict(a.granted_op);
endrule
endmodule endmodule
endinstance endinstance
@ -73,14 +91,13 @@ endinstance
module mkPriorityMemArbiter(MemArbiter#(num_clients, addr)) module mkPriorityMemArbiter(MemArbiter#(num_clients, addr))
provisos (Bits#(addr, _), provisos (Bits#(addr, _),
Eq#(addr), Eq#(addr),
Min#(num_clients, 1, 1), Min#(num_clients, 1, 1));
Alias#(client_idx, UInt#(TLog#(num_clients))));
Vector#(num_clients, RWire#(MemArbiterOp#(addr))) reqs <- replicateM(mkRWire()); Vector#(num_clients, RWire#(MemArbiterOp#(addr))) reqs <- replicateM(mkRWire());
Wire#(Vector#(num_clients, Bool)) grants <- mkBypassWire(); Wire#(Vector#(num_clients, Bool)) grants <- mkBypassWire();
RWire#(MemArbiterOp#(addr)) conflict_op <- mkRWire(); RWire#(MemArbiterOp#(addr)) conflict_in <- mkRWire();
RWire#(client_idx) granted_idx <- mkRWire(); RWire#(MemArbiterOp#(addr)) granted_op_out <- mkRWire();
(* no_implicit_conditions, fire_when_enabled *) (* no_implicit_conditions, fire_when_enabled *)
rule grant_requests; rule grant_requests;
@ -89,11 +106,11 @@ module mkPriorityMemArbiter(MemArbiter#(num_clients, addr))
for (Integer i=0; i<valueOf(num_clients); i=i+1) begin for (Integer i=0; i<valueOf(num_clients); i=i+1) begin
if (reqs[i].wget() matches tagged Valid .req &&& if (reqs[i].wget() matches tagged Valid .req &&&
!mem_ops_conflict(conflict_op.wget(), reqs[i].wget()) &&& !mem_ops_conflict(conflict_in.wget(), reqs[i].wget()) &&&
!done) begin !done) begin
done = True; done = True;
grant[i] = True; grant[i] = True;
granted_idx.wset(fromInteger(i)); granted_op_out.wset(req);
end end
end end
@ -108,29 +125,24 @@ module mkPriorityMemArbiter(MemArbiter#(num_clients, addr))
endinterface); endinterface);
interface ports = _ifcs; interface ports = _ifcs;
method client_idx granted_port() if (granted_idx.wget() matches tagged Valid .idx); method conflict = conflict_in.wset;
return idx; method MemArbiterOp#(addr) granted_op() if (granted_op_out.wget() matches tagged Valid .op);
endmethod
method MemArbiterOp#(addr) conflict_out() if (granted_idx.wget() matches tagged Valid .idx &&&
reqs[idx].wget() matches tagged Valid .op);
return op; return op;
endmethod endmethod
method conflict_in = conflict_op.wset;
endmodule endmodule
typedef struct { typedef struct {
Vector#(n, Bool) grant_vec; Vector#(n, Bool) grant_vec;
Maybe#(UInt#(TLog#(n))) granted_idx; Maybe#(MemArbiterOp#(addr)) granted_op;
} GrantResult#(numeric type n, type addr) deriving (Bits, Eq, FShow); } GrantResult#(numeric type n, type addr) deriving (Bits, Eq, FShow);
// select_grant computes which one entry of requests should be // select_grant computes which one entry of requests should be
// granted. Priority order is descending starting from // granted. Priority order is descending starting from
// requests[hipri]. // requests[hipri].
function GrantResult#(n, addr) select_grant(Vector#(n, Maybe#(MemArbiterOp#(addr))) requests, function GrantResult#(n, addr) select_grant(Vector#(n, Maybe#(MemArbiterOp#(addr))) requests,
client_idx hipri, UInt#(TLog#(n)) hipri,
Maybe#(MemArbiterOp#(addr)) conflict) Maybe#(MemArbiterOp#(addr)) conflict)
provisos (Eq#(addr), provisos (Eq#(addr));
Alias#(client_idx, UInt#(TLog#(n))));
function onehot(idx); function onehot(idx);
let ret = replicate(False); let ret = replicate(False);
@ -139,15 +151,13 @@ function GrantResult#(n, addr) select_grant(Vector#(n, Maybe#(MemArbiterOp#(addr
endfunction endfunction
function GrantResult#(n, addr) do_fold(GrantResult#(n, addr) acc, function GrantResult#(n, addr) do_fold(GrantResult#(n, addr) acc,
Tuple2#(client_idx, Tuple2#(UInt#(TLog#(n)),
Maybe#(MemArbiterOp#(addr))) next); Maybe#(MemArbiterOp#(addr))) next);
match {.idx, .mreq} = next; match {.idx, .mreq} = next;
if (mreq matches tagged Valid .req &&& if (mreq matches tagged Valid .req &&& acc.granted_op matches tagged Invalid &&& !mem_ops_conflict(conflict, mreq))
acc.granted_idx matches tagged Invalid &&&
!mem_ops_conflict(conflict, mreq))
return GrantResult{ return GrantResult{
grant_vec: onehot(idx), grant_vec: onehot(idx),
granted_idx: tagged Valid idx granted_op: tagged Valid req
}; };
else else
// Previous grant won, not requesting, or request not satisfiable. // Previous grant won, not requesting, or request not satisfiable.
@ -158,7 +168,7 @@ function GrantResult#(n, addr) select_grant(Vector#(n, Maybe#(MemArbiterOp#(addr
let rot = rotateBy(in, fromInteger(valueOf(n)-1)-hipri+1); let rot = rotateBy(in, fromInteger(valueOf(n)-1)-hipri+1);
let seed = GrantResult{ let seed = GrantResult{
grant_vec: replicate(False), grant_vec: replicate(False),
granted_idx: tagged Invalid granted_op: tagged Invalid
}; };
return foldl(do_fold, seed, rot); return foldl(do_fold, seed, rot);
endfunction endfunction
@ -166,20 +176,19 @@ endfunction
module mkRoundRobinMemArbiter(MemArbiter#(num_clients, addr)) module mkRoundRobinMemArbiter(MemArbiter#(num_clients, addr))
provisos (Bits#(addr, _), provisos (Bits#(addr, _),
Eq#(addr), Eq#(addr),
Min#(num_clients, 1, 1), Min#(num_clients, 1, 1));
Alias#(client_idx, UInt#(TLog#(num_clients))));
Vector#(num_clients, RWire#(MemArbiterOp#(addr))) reqs <- replicateM(mkRWire); Vector#(num_clients, RWire#(MemArbiterOp#(addr))) reqs <- replicateM(mkRWire);
Wire#(Vector#(num_clients, Bool)) grants <- mkBypassWire(); Wire#(Vector#(num_clients, Bool)) grants <- mkBypassWire();
RWire#(MemArbiterOp#(addr)) conflict_op <- mkRWire(); RWire#(MemArbiterOp#(addr)) conflict_in <- mkRWire();
RWire#(client_idx) granted_idx_out <- mkRWire(); RWire#(MemArbiterOp#(addr)) granted_op_out <- mkRWire();
// high_prio is the index of the client that should be first in // high_prio is the index of the client that should be first in
// line to receive access. Every time we grant access to a client, // line to receive access. Every time we grant access to a client,
// the one after that in sequence becomes high_prio in the next // the one after that in sequence becomes high_prio in the next
// round. // round.
Reg#(client_idx) high_prio <- mkReg(0); Reg#(UInt#(TLog#(num_clients))) high_prio <- mkReg(0);
function Maybe#(_t) get_mreq(RWire#(_t) w); function Maybe#(_t) get_mreq(RWire#(_t) w);
return w.wget(); return w.wget();
@ -187,11 +196,11 @@ module mkRoundRobinMemArbiter(MemArbiter#(num_clients, addr))
rule grant; rule grant;
let in = map(get_mreq, reqs); let in = map(get_mreq, reqs);
let res = select_grant(in, high_prio, conflict_op.wget()); let res = select_grant(in, high_prio, conflict_in.wget());
grants <= res.grant_vec; grants <= res.grant_vec;
if (res.granted_idx matches tagged Valid .idx) begin if (res.granted_op matches tagged Valid .op) begin
granted_idx_out.wset(idx); granted_op_out.wset(op);
high_prio <= validValue(findElem(True, rotateR(res.grant_vec))); high_prio <= validValue(findElem(True, rotateR(res.grant_vec)));
end end
endrule endrule
@ -204,14 +213,10 @@ module mkRoundRobinMemArbiter(MemArbiter#(num_clients, addr))
endinterface); endinterface);
interface ports = _ifcs; interface ports = _ifcs;
method client_idx granted_port() if (granted_idx_out.wget() matches tagged Valid .idx); method conflict = conflict_in.wset;
return idx; method MemArbiterOp#(addr) granted_op() if (granted_op_out.wget() matches tagged Valid .op);
endmethod
method MemArbiterOp#(addr) conflict_out() if (granted_idx_out.wget() matches tagged Valid .idx &&&
reqs[idx].wget() matches tagged Valid .op);
return op; return op;
endmethod endmethod
method conflict_in = conflict_op.wset;
endmodule endmodule
endpackage endpackage

View File

@ -19,7 +19,7 @@ typedef struct {
Maybe#(MemArbiterOp#(Addr)) conflict; Maybe#(MemArbiterOp#(Addr)) conflict;
Vector#(n, Bool) want_grants; Vector#(n, Bool) want_grants;
Maybe#(MemArbiterOp#(Addr)) want_conflict_out; Maybe#(MemArbiterOp#(Addr)) want_granted_op;
} TestCase#(numeric type n) deriving (Bits, Eq); } TestCase#(numeric type n) deriving (Bits, Eq);
function Maybe#(MemArbiterOp#(Addr)) read(Addr addr); function Maybe#(MemArbiterOp#(Addr)) read(Addr addr);
@ -54,13 +54,13 @@ function TestCase#(n) testCase(String name,
Vector#(n, Maybe#(MemArbiterOp#(Addr))) reqs, Vector#(n, Maybe#(MemArbiterOp#(Addr))) reqs,
Maybe#(MemArbiterOp#(Addr)) conflict, Maybe#(MemArbiterOp#(Addr)) conflict,
Vector#(n, Bool) want_grants, Vector#(n, Bool) want_grants,
Maybe#(MemArbiterOp#(Addr)) want_conflict_out); Maybe#(MemArbiterOp#(Addr)) want_granted_op);
return TestCase{ return TestCase{
name: name, name: name,
reqs: reqs, reqs: reqs,
conflict: conflict, conflict: conflict,
want_grants: want_grants, want_grants: want_grants,
want_conflict_out: want_conflict_out want_granted_op: want_granted_op
}; };
endfunction endfunction
@ -84,14 +84,14 @@ module mkArbiterTB(MemArbiter#(n, Addr) dut, Vector#(m, TestCase#(n)) tests, TB
(* no_implicit_conditions, fire_when_enabled *) (* no_implicit_conditions, fire_when_enabled *)
rule forbid (running && isValid(tests[idx].conflict)); rule forbid (running && isValid(tests[idx].conflict));
dut.conflict_in(validValue(tests[idx].conflict)); dut.conflict(validValue(tests[idx].conflict));
endrule endrule
Wire#(Maybe#(MemArbiterOp#(Addr))) got_conflict_out <- mkDWire(tagged Invalid); Wire#(Maybe#(MemArbiterOp#(Addr))) got_granted_op <- mkDWire(tagged Invalid);
(* fire_when_enabled *) (* fire_when_enabled *)
rule collect_conflict_out (running); rule collect_granted_op (running);
got_conflict_out <= tagged Valid dut.conflict_out(); got_granted_op <= tagged Valid dut.granted_op();
endrule endrule
function Fmt req_s(Maybe#(MemArbiterOp#(Addr)) v); function Fmt req_s(Maybe#(MemArbiterOp#(Addr)) v);
@ -107,13 +107,13 @@ module mkArbiterTB(MemArbiter#(n, Addr) dut, Vector#(m, TestCase#(n)) tests, TB
let test = tests[idx]; let test = tests[idx];
let reqs = test.reqs; let reqs = test.reqs;
let want_grants = test.want_grants; let want_grants = test.want_grants;
let want_conflict_out = test.want_conflict_out; let want_granted_op = test.want_granted_op;
Vector#(n, Bool) got_grants = newVector; Vector#(n, Bool) got_grants = newVector;
for (Integer i=0; i<valueOf(n); i=i+1) for (Integer i=0; i<valueOf(n); i=i+1)
got_grants[i] = dut.ports[i].grant(); got_grants[i] = dut.ports[i].grant();
$display("RUN %s (%0d)", tests[idx].name, idx); $display("RUN %s (%0d)", tests[idx].name, idx);
if (got_grants != want_grants || got_conflict_out != want_conflict_out) begin if (got_grants != want_grants || got_granted_op != want_granted_op) begin
$display("input:"); $display("input:");
for (Integer i=0; i<valueOf(n); i=i+1) for (Integer i=0; i<valueOf(n); i=i+1)
$display(" ", $format("%0d", i), ": ", req_s(reqs[i])); $display(" ", $format("%0d", i), ": ", req_s(reqs[i]));
@ -121,10 +121,10 @@ module mkArbiterTB(MemArbiter#(n, Addr) dut, Vector#(m, TestCase#(n)) tests, TB
$display(" output:"); $display(" output:");
$display(" grants: ", fshow(got_grants)); $display(" grants: ", fshow(got_grants));
$display(" granted: ", fshow(got_conflict_out)); $display(" granted: ", fshow(got_granted_op));
$display(" want grants: ", fshow(tests[idx].want_grants)); $display(" want grants: ", fshow(tests[idx].want_grants));
$display(" want granted: ", fshow(want_conflict_out)); $display(" want granted: ", fshow(want_granted_op));
dynamicAssert(False, "wrong arbiter output"); dynamicAssert(False, "wrong arbiter output");
end end

View File

@ -1,99 +0,0 @@
package VRAM;
import Connectable::*;
import GetPut::*;
import ClientServer::*;
import Vector::*;
import FIFOF::*;
import SpecialFIFOs::*;
import MemArbiter::*;
import VRAMCore::*;
// Re-exports from VRAMCore
export VRAMAddr, VRAMData, VRAMRequest(..), VRAMResponse(..);
export VRAMServer(..);
export VRAM(..), mkVRAM;
typedef Server#(VRAMRequest, VRAMResponse) VRAMServer;
// mkArbitratedVRAMServers expands a VRAMServer port into multiple
// ports through the use of a MemArbiter.
module mkArbitratedVRAMServers(VRAMServer ram, MemArbiter#(n, VRAMAddr) arb, Vector#(n, VRAMServer) ifc)
provisos (Min#(n, 1, 1),
Alias#(port_idx, UInt#(TLog#(n))));
Vector#(n, FIFOF#(VRAMRequest)) requests <- replicateM(mkBypassFIFOF());
Vector#(n, FIFOF#(VRAMResponse)) responses <- replicateM(mkBypassFIFOF());
Reg#(Maybe#(port_idx)) awaiting_response[2] <- mkCReg(2, tagged Invalid);
(* fire_when_enabled *)
rule request_ports;
for (Integer i=0; i<valueOf(n); i=i+1)
if (requests[i].notEmpty) begin
let req = requests[i].first;
let arb_req = MemArbiterOp{
write: isValid(req.data),
addr: req.addr
};
arb.ports[i].request(arb_req);
end
endrule
(* fire_when_enabled *)
rule submit (awaiting_response[1] matches tagged Invalid);
let port = arb.granted_port();
ram.request.put(requests[port].first);
requests[port].deq();
awaiting_response[1] <= tagged Valid port;
endrule
(* fire_when_enabled *)
rule response (awaiting_response[0] matches tagged Valid .port &&& responses[port].notFull);
let resp <- ram.response.get();
responses[port].enq(resp);
awaiting_response[0] <= tagged Invalid;
endrule
return map(uncurry(toGPServer), zip(requests, responses));
endmodule
// VRAM is a GARY video RAM and its memory ports.
interface VRAM;
interface VRAMServer cpu;
interface VRAMServer debugger;
interface VRAMServer palette;
interface VRAMServer tile1;
interface VRAMServer tile2;
interface VRAMServer sprite;
endinterface
// mkVRAM constructs a VRAM of the requested size. Memory access is
// spread across two internal ports as follows:
//
// Port A: strict most-important-wins priority: CPU, then debugger,
// then palette DAC.
// Port B: equal round-robin prioritization between two tile engines
// and the sprite engine.
module mkVRAM(Integer num_kilobytes, VRAM ifc);
VRAMCore ram <- mkVRAMCore(num_kilobytes);
MemArbiter#(3, VRAMAddr) arbA <- mkPriorityMemArbiter();
Vector#(3, VRAMServer) portA <- mkArbitratedVRAMServers(ram.portA, arbA);
MemArbiter#(3, VRAMAddr) arbB <- mkRoundRobinMemArbiter();
Vector#(3, VRAMServer) portB <- mkArbitratedVRAMServers(ram.portB, arbB);
// Connect up the arbiters so they correctly prevent write-write
// and write-read conflicts.
mkConnection(arbA, arbB);
interface cpu = portA[0];
interface debugger = portA[1];
interface palette = portA[2];
interface tile1 = portB[0];
interface tile2 = portB[1];
interface sprite = portB[2];
endmodule
endpackage

View File

@ -1,82 +1,48 @@
package VRAMCore; package VRAMCore;
import Connectable::*;
import GetPut::*; import GetPut::*;
import ClientServer::*; import ClientServer::*;
import BRAMCore::*; import DReg::*;
import BRAM::*;
import Vector::*;
import FIFOF::*;
import SpecialFIFOs::*;
import Real::*; import Real::*;
import Printf::*;
import DelayLine::*; import DelayLine::*;
import ECP5_RAM::*; import ECP5_RAM::*;
export VRAMAddr; export VRAMAddr;
export VRAMData; export VRAMData;
export VRAMRequest(..); export VRAMRequest;
export VRAMResponse(..); export VRAMResponse;
export VRAMCore(..); export VRAMClient;
export VRAMServer;
export VRAMCore;
export mkVRAMCore; export mkVRAMCore;
typedef Bit#(8) VRAMData; typedef Bit#(8) VRAMData;
typedef UInt#(17) VRAMAddr; // Each byte RAM we build below can address 4096 bytes, which is 12
typedef UInt#(2) ArrayAddr; // address bits.
typedef UInt#(3) ChipAddr;
typedef UInt#(12) ByteAddr; typedef UInt#(12) ByteAddr;
typedef UInt#(3) ChipAddr;
// ByteRAM is two EBRs glued together to make a whole-byte memory. // ByteRAM is two EBRs glued together to make a whole-byte memory.
typedef EBR#(ByteAddr, VRAMData, ByteAddr, VRAMData) ByteRAM; typedef EBR#(ByteAddr, VRAMData, ByteAddr, VRAMData) ByteRAM;
typedef struct {
VRAMAddr addr;
Maybe#(VRAMData) data;
} VRAMRequest deriving (Bits, Eq);
typedef struct {
VRAMData data;
} VRAMResponse deriving (Bits, Eq);
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 // mkByteRAM glues two ECP5 EBRs together to make a 4096x8b memory
// block. Like the underlying ECP5 EBRs, callers must bring their own // block. Like the underlying ECP5 EBRs, callers must bring their own
// flow control to read out responses one cycle after putting a read // flow control to read out responses one cycle after putting a read
// request. // request.
module mkByteRAM(ChipAddr chip_addr, ByteRAM ifc); module mkByteRAM(ChipAddr chip_addr, ByteRAM ifc);
EBR#(ByteAddr, Bit#(4), ByteAddr, Bit#(4)) upper <- mkNibbleRAM(chip_addr); EBRPortConfig cfg = defaultValue;
EBR#(ByteAddr, Bit#(4), ByteAddr, Bit#(4)) lower <- mkNibbleRAM(chip_addr); 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; interface EBRPort portA;
method Action put(ChipAddr chip_select, Bool write, ByteAddr addr, VRAMData data_in); method Action put(ChipAddr chip_select, Bool write, ByteAddr addr, VRAMData data_in);
@ -153,9 +119,25 @@ module mkByteRAMArray(Integer num_chips, ByteRAM ifc);
endinterface endinterface
endmodule endmodule
typedef UInt#(2) ArrayAddr;
typedef UInt#(17) VRAMAddr;
typedef struct {
VRAMAddr addr;
Maybe#(VRAMData) data;
} VRAMRequest deriving (Bits, Eq);
typedef struct {
VRAMData data;
} VRAMResponse deriving (Bits, Eq);
typedef Server#(VRAMRequest, VRAMResponse) VRAMServer;
typedef Client#(VRAMRequest, VRAMResponse) VRAMClient;
interface VRAMCore; interface VRAMCore;
interface Server#(VRAMRequest, VRAMResponse) portA; interface VRAMServer portA;
interface Server#(VRAMRequest, VRAMResponse) portB; interface VRAMServer portB;
endinterface endinterface
// mkVRAMCore creates a dual port VRAM of the specified size, using // mkVRAMCore creates a dual port VRAM of the specified size, using
@ -181,7 +163,11 @@ module mkVRAMCore(Integer num_kilobytes, VRAMCore ifc);
let num_arrays = ceil(fromInteger(num_byterams) / 8); let num_arrays = ceil(fromInteger(num_byterams) / 8);
function Tuple3#(ArrayAddr, ChipAddr, ByteAddr) split_addr(VRAMAddr a); function Tuple3#(ArrayAddr, ChipAddr, ByteAddr) split_addr(VRAMAddr a);
return unpack(pack(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 endfunction
ByteRAM arrays[num_arrays]; ByteRAM arrays[num_arrays];
@ -193,7 +179,7 @@ module mkVRAMCore(Integer num_kilobytes, VRAMCore ifc);
Reg#(Maybe#(ArrayAddr)) inflight_A[2] <- mkCReg(2, tagged Invalid); Reg#(Maybe#(ArrayAddr)) inflight_A[2] <- mkCReg(2, tagged Invalid);
Reg#(Maybe#(ArrayAddr)) inflight_B[2] <- mkCReg(2, tagged Invalid); Reg#(Maybe#(ArrayAddr)) inflight_B[2] <- mkCReg(2, tagged Invalid);
interface Server portA; interface VRAMServer portA;
interface Put request; interface Put request;
method Action put(VRAMRequest req) if (inflight_A[1] matches tagged Invalid); method Action put(VRAMRequest req) if (inflight_A[1] matches tagged Invalid);
match {.array, .chip, .byteaddr} = split_addr(req.addr); match {.array, .chip, .byteaddr} = split_addr(req.addr);
@ -210,7 +196,7 @@ module mkVRAMCore(Integer num_kilobytes, VRAMCore ifc);
endinterface endinterface
endinterface endinterface
interface Server portB; interface VRAMServer portB;
interface Put request; interface Put request;
method Action put(VRAMRequest req) if (inflight_B[1] matches tagged Invalid); method Action put(VRAMRequest req) if (inflight_B[1] matches tagged Invalid);
match {.array, .chip, .byteaddr} = split_addr(req.addr); match {.array, .chip, .byteaddr} = split_addr(req.addr);