Compare commits
5 Commits
913c407224
...
1929bbe3cc
Author | SHA1 | Date |
---|---|---|
David Anderson | 1929bbe3cc | |
David Anderson | fb57903021 | |
David Anderson | 79b54ca86f | |
David Anderson | 2ebf399d62 | |
David Anderson | eca95e0fb6 |
|
@ -5,7 +5,6 @@ import Vector::*;
|
|||
|
||||
export MemArbiterOp(..);
|
||||
export MemArbiterServer(..);
|
||||
export MemArbiterClient(..);
|
||||
export MemArbiter(..), mkPriorityMemArbiter, mkRoundRobinMemArbiter;
|
||||
|
||||
// A MemArbiterOp is an operation that a client is seeking permission
|
||||
|
@ -15,6 +14,8 @@ typedef struct {
|
|||
addr addr;
|
||||
} 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)
|
||||
provisos(Eq#(addr));
|
||||
|
||||
|
@ -31,58 +32,39 @@ interface MemArbiterServer#(type addr);
|
|||
method Bool grant();
|
||||
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.
|
||||
interface MemArbiter#(numeric type num_clients, type addr);
|
||||
// ports allow clients to request memory access.
|
||||
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.
|
||||
//
|
||||
// Suppose you're arbitrating access to a dual-port
|
||||
// memory. Typically, such a memory specifies that if one port is
|
||||
// writing to an address, the other must not concurrently read or
|
||||
// write that same address. This means the arbiters attached to
|
||||
// each memory port must cooperate to avoid simultaneously granting
|
||||
// Suppose you're arbitrating access to a dual-port memory.
|
||||
// Typically, such a memory specifies that if one port is writing
|
||||
// to an address, the other must not concurrently read or write
|
||||
// that same address. This means the arbiters attached to each
|
||||
// memory port must cooperate to avoid simultaneously granting
|
||||
// conflicting requests from their clients.
|
||||
//
|
||||
// Calling conflict prevents the arbiter from granting a concurrent
|
||||
// request that would result in a write-write, read-write or
|
||||
// write-read conflict. granted_op emits the operation that the
|
||||
// arbiter is granting, if any.
|
||||
// conflict_in supplies an already granted operation that this
|
||||
// arbiter must avoid conflicting with. conflict_out emits the
|
||||
// operation that the arbiter is granting, if any.
|
||||
//
|
||||
// MemArbiter intances are Connectable: mkConnection(a, b) gives
|
||||
// conflict priority to a. That is, b only grants requests that
|
||||
// don't conflict with a's grant.
|
||||
// mkConnection(firstArbiter, secondArbiter) gives conflict
|
||||
// priority to firstArbiter. That is, secondArbiter only grants
|
||||
// requests that don't conflict with grants made by firstArbiter.
|
||||
(* always_ready *)
|
||||
method Action conflict(MemArbiterOp#(addr) conflict);
|
||||
method MemArbiterOp#(addr) granted_op();
|
||||
method Action conflict_in(MemArbiterOp#(addr) conflict);
|
||||
method MemArbiterOp#(addr) conflict_out();
|
||||
endinterface
|
||||
|
||||
instance Connectable#(MemArbiter#(m, addr), MemArbiter#(n, addr));
|
||||
module mkConnection(MemArbiter#(m, addr) a, MemArbiter#(n, addr) b, Empty ifc);
|
||||
(* fire_when_enabled *)
|
||||
rule forward_conflict;
|
||||
b.conflict(a.granted_op);
|
||||
endrule
|
||||
mkConnection(a.conflict_out, b.conflict_in);
|
||||
endmodule
|
||||
endinstance
|
||||
|
||||
|
@ -91,13 +73,14 @@ endinstance
|
|||
module mkPriorityMemArbiter(MemArbiter#(num_clients, addr))
|
||||
provisos (Bits#(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());
|
||||
Wire#(Vector#(num_clients, Bool)) grants <- mkBypassWire();
|
||||
|
||||
RWire#(MemArbiterOp#(addr)) conflict_in <- mkRWire();
|
||||
RWire#(MemArbiterOp#(addr)) granted_op_out <- mkRWire();
|
||||
RWire#(MemArbiterOp#(addr)) conflict_op <- mkRWire();
|
||||
RWire#(client_idx) granted_idx <- mkRWire();
|
||||
|
||||
(* no_implicit_conditions, fire_when_enabled *)
|
||||
rule grant_requests;
|
||||
|
@ -106,11 +89,11 @@ module mkPriorityMemArbiter(MemArbiter#(num_clients, addr))
|
|||
|
||||
for (Integer i=0; i<valueOf(num_clients); i=i+1) begin
|
||||
if (reqs[i].wget() matches tagged Valid .req &&&
|
||||
!mem_ops_conflict(conflict_in.wget(), reqs[i].wget()) &&&
|
||||
!mem_ops_conflict(conflict_op.wget(), reqs[i].wget()) &&&
|
||||
!done) begin
|
||||
done = True;
|
||||
grant[i] = True;
|
||||
granted_op_out.wset(req);
|
||||
granted_idx.wset(fromInteger(i));
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -125,24 +108,29 @@ module mkPriorityMemArbiter(MemArbiter#(num_clients, addr))
|
|||
endinterface);
|
||||
|
||||
interface ports = _ifcs;
|
||||
method conflict = conflict_in.wset;
|
||||
method MemArbiterOp#(addr) granted_op() if (granted_op_out.wget() matches tagged Valid .op);
|
||||
method client_idx granted_port() if (granted_idx.wget() matches tagged Valid .idx);
|
||||
return idx;
|
||||
endmethod
|
||||
method MemArbiterOp#(addr) conflict_out() if (granted_idx.wget() matches tagged Valid .idx &&&
|
||||
reqs[idx].wget() matches tagged Valid .op);
|
||||
return op;
|
||||
endmethod
|
||||
method conflict_in = conflict_op.wset;
|
||||
endmodule
|
||||
|
||||
typedef struct {
|
||||
Vector#(n, Bool) grant_vec;
|
||||
Maybe#(MemArbiterOp#(addr)) granted_op;
|
||||
Maybe#(UInt#(TLog#(n))) granted_idx;
|
||||
} GrantResult#(numeric type n, type addr) deriving (Bits, Eq, FShow);
|
||||
|
||||
// select_grant computes which one entry of requests should be
|
||||
// granted. Priority order is descending starting from
|
||||
// requests[hipri].
|
||||
function GrantResult#(n, addr) select_grant(Vector#(n, Maybe#(MemArbiterOp#(addr))) requests,
|
||||
UInt#(TLog#(n)) hipri,
|
||||
client_idx hipri,
|
||||
Maybe#(MemArbiterOp#(addr)) conflict)
|
||||
provisos (Eq#(addr));
|
||||
provisos (Eq#(addr),
|
||||
Alias#(client_idx, UInt#(TLog#(n))));
|
||||
|
||||
function onehot(idx);
|
||||
let ret = replicate(False);
|
||||
|
@ -151,13 +139,15 @@ function GrantResult#(n, addr) select_grant(Vector#(n, Maybe#(MemArbiterOp#(addr
|
|||
endfunction
|
||||
|
||||
function GrantResult#(n, addr) do_fold(GrantResult#(n, addr) acc,
|
||||
Tuple2#(UInt#(TLog#(n)),
|
||||
Tuple2#(client_idx,
|
||||
Maybe#(MemArbiterOp#(addr))) next);
|
||||
match {.idx, .mreq} = next;
|
||||
if (mreq matches tagged Valid .req &&& acc.granted_op matches tagged Invalid &&& !mem_ops_conflict(conflict, mreq))
|
||||
if (mreq matches tagged Valid .req &&&
|
||||
acc.granted_idx matches tagged Invalid &&&
|
||||
!mem_ops_conflict(conflict, mreq))
|
||||
return GrantResult{
|
||||
grant_vec: onehot(idx),
|
||||
granted_op: tagged Valid req
|
||||
granted_idx: tagged Valid idx
|
||||
};
|
||||
else
|
||||
// Previous grant won, not requesting, or request not satisfiable.
|
||||
|
@ -168,7 +158,7 @@ function GrantResult#(n, addr) select_grant(Vector#(n, Maybe#(MemArbiterOp#(addr
|
|||
let rot = rotateBy(in, fromInteger(valueOf(n)-1)-hipri+1);
|
||||
let seed = GrantResult{
|
||||
grant_vec: replicate(False),
|
||||
granted_op: tagged Invalid
|
||||
granted_idx: tagged Invalid
|
||||
};
|
||||
return foldl(do_fold, seed, rot);
|
||||
endfunction
|
||||
|
@ -176,19 +166,20 @@ endfunction
|
|||
module mkRoundRobinMemArbiter(MemArbiter#(num_clients, addr))
|
||||
provisos (Bits#(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);
|
||||
Wire#(Vector#(num_clients, Bool)) grants <- mkBypassWire();
|
||||
|
||||
RWire#(MemArbiterOp#(addr)) conflict_in <- mkRWire();
|
||||
RWire#(MemArbiterOp#(addr)) granted_op_out <- mkRWire();
|
||||
RWire#(MemArbiterOp#(addr)) conflict_op <- mkRWire();
|
||||
RWire#(client_idx) granted_idx_out <- mkRWire();
|
||||
|
||||
// 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,
|
||||
// the one after that in sequence becomes high_prio in the next
|
||||
// round.
|
||||
Reg#(UInt#(TLog#(num_clients))) high_prio <- mkReg(0);
|
||||
Reg#(client_idx) high_prio <- mkReg(0);
|
||||
|
||||
function Maybe#(_t) get_mreq(RWire#(_t) w);
|
||||
return w.wget();
|
||||
|
@ -196,11 +187,11 @@ module mkRoundRobinMemArbiter(MemArbiter#(num_clients, addr))
|
|||
|
||||
rule grant;
|
||||
let in = map(get_mreq, reqs);
|
||||
let res = select_grant(in, high_prio, conflict_in.wget());
|
||||
let res = select_grant(in, high_prio, conflict_op.wget());
|
||||
|
||||
grants <= res.grant_vec;
|
||||
if (res.granted_op matches tagged Valid .op) begin
|
||||
granted_op_out.wset(op);
|
||||
if (res.granted_idx matches tagged Valid .idx) begin
|
||||
granted_idx_out.wset(idx);
|
||||
high_prio <= validValue(findElem(True, rotateR(res.grant_vec)));
|
||||
end
|
||||
endrule
|
||||
|
@ -213,10 +204,14 @@ module mkRoundRobinMemArbiter(MemArbiter#(num_clients, addr))
|
|||
endinterface);
|
||||
|
||||
interface ports = _ifcs;
|
||||
method conflict = conflict_in.wset;
|
||||
method MemArbiterOp#(addr) granted_op() if (granted_op_out.wget() matches tagged Valid .op);
|
||||
method client_idx granted_port() if (granted_idx_out.wget() matches tagged Valid .idx);
|
||||
return idx;
|
||||
endmethod
|
||||
method MemArbiterOp#(addr) conflict_out() if (granted_idx_out.wget() matches tagged Valid .idx &&&
|
||||
reqs[idx].wget() matches tagged Valid .op);
|
||||
return op;
|
||||
endmethod
|
||||
method conflict_in = conflict_op.wset;
|
||||
endmodule
|
||||
|
||||
endpackage
|
||||
|
|
|
@ -19,7 +19,7 @@ typedef struct {
|
|||
Maybe#(MemArbiterOp#(Addr)) conflict;
|
||||
|
||||
Vector#(n, Bool) want_grants;
|
||||
Maybe#(MemArbiterOp#(Addr)) want_granted_op;
|
||||
Maybe#(MemArbiterOp#(Addr)) want_conflict_out;
|
||||
} TestCase#(numeric type n) deriving (Bits, Eq);
|
||||
|
||||
function Maybe#(MemArbiterOp#(Addr)) read(Addr addr);
|
||||
|
@ -54,13 +54,13 @@ function TestCase#(n) testCase(String name,
|
|||
Vector#(n, Maybe#(MemArbiterOp#(Addr))) reqs,
|
||||
Maybe#(MemArbiterOp#(Addr)) conflict,
|
||||
Vector#(n, Bool) want_grants,
|
||||
Maybe#(MemArbiterOp#(Addr)) want_granted_op);
|
||||
Maybe#(MemArbiterOp#(Addr)) want_conflict_out);
|
||||
return TestCase{
|
||||
name: name,
|
||||
reqs: reqs,
|
||||
conflict: conflict,
|
||||
want_grants: want_grants,
|
||||
want_granted_op: want_granted_op
|
||||
want_conflict_out: want_conflict_out
|
||||
};
|
||||
endfunction
|
||||
|
||||
|
@ -84,14 +84,14 @@ module mkArbiterTB(MemArbiter#(n, Addr) dut, Vector#(m, TestCase#(n)) tests, TB
|
|||
|
||||
(* no_implicit_conditions, fire_when_enabled *)
|
||||
rule forbid (running && isValid(tests[idx].conflict));
|
||||
dut.conflict(validValue(tests[idx].conflict));
|
||||
dut.conflict_in(validValue(tests[idx].conflict));
|
||||
endrule
|
||||
|
||||
Wire#(Maybe#(MemArbiterOp#(Addr))) got_granted_op <- mkDWire(tagged Invalid);
|
||||
Wire#(Maybe#(MemArbiterOp#(Addr))) got_conflict_out <- mkDWire(tagged Invalid);
|
||||
|
||||
(* fire_when_enabled *)
|
||||
rule collect_granted_op (running);
|
||||
got_granted_op <= tagged Valid dut.granted_op();
|
||||
rule collect_conflict_out (running);
|
||||
got_conflict_out <= tagged Valid dut.conflict_out();
|
||||
endrule
|
||||
|
||||
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 reqs = test.reqs;
|
||||
let want_grants = test.want_grants;
|
||||
let want_granted_op = test.want_granted_op;
|
||||
let want_conflict_out = test.want_conflict_out;
|
||||
Vector#(n, Bool) got_grants = newVector;
|
||||
for (Integer i=0; i<valueOf(n); i=i+1)
|
||||
got_grants[i] = dut.ports[i].grant();
|
||||
|
||||
$display("RUN %s (%0d)", tests[idx].name, idx);
|
||||
if (got_grants != want_grants || got_granted_op != want_granted_op) begin
|
||||
if (got_grants != want_grants || got_conflict_out != want_conflict_out) begin
|
||||
$display("input:");
|
||||
for (Integer i=0; i<valueOf(n); i=i+1)
|
||||
$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(" grants: ", fshow(got_grants));
|
||||
$display(" granted: ", fshow(got_granted_op));
|
||||
$display(" granted: ", fshow(got_conflict_out));
|
||||
|
||||
$display(" want grants: ", fshow(tests[idx].want_grants));
|
||||
$display(" want granted: ", fshow(want_granted_op));
|
||||
$display(" want granted: ", fshow(want_conflict_out));
|
||||
dynamicAssert(False, "wrong arbiter output");
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
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
|
|
@ -1,48 +1,82 @@
|
|||
package VRAMCore;
|
||||
|
||||
import Connectable::*;
|
||||
import GetPut::*;
|
||||
import ClientServer::*;
|
||||
import DReg::*;
|
||||
import BRAM::*;
|
||||
import Vector::*;
|
||||
import FIFOF::*;
|
||||
import SpecialFIFOs::*;
|
||||
import BRAMCore::*;
|
||||
import Real::*;
|
||||
import Printf::*;
|
||||
|
||||
import DelayLine::*;
|
||||
import ECP5_RAM::*;
|
||||
|
||||
export VRAMAddr;
|
||||
export VRAMData;
|
||||
export VRAMRequest;
|
||||
export VRAMResponse;
|
||||
export VRAMClient;
|
||||
export VRAMServer;
|
||||
export VRAMCore;
|
||||
export VRAMRequest(..);
|
||||
export VRAMResponse(..);
|
||||
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#(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);
|
||||
|
||||
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
|
||||
// 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);
|
||||
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);
|
||||
|
@ -119,25 +153,9 @@ module mkByteRAMArray(Integer num_chips, ByteRAM ifc);
|
|||
endinterface
|
||||
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 VRAMServer portA;
|
||||
interface VRAMServer portB;
|
||||
interface Server#(VRAMRequest, VRAMResponse) portA;
|
||||
interface Server#(VRAMRequest, VRAMResponse) portB;
|
||||
endinterface
|
||||
|
||||
// mkVRAMCore creates a dual port VRAM of the specified size, using
|
||||
|
@ -163,11 +181,7 @@ module mkVRAMCore(Integer num_kilobytes, VRAMCore ifc);
|
|||
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));
|
||||
return unpack(pack(a));
|
||||
endfunction
|
||||
|
||||
ByteRAM arrays[num_arrays];
|
||||
|
@ -179,7 +193,7 @@ module mkVRAMCore(Integer num_kilobytes, VRAMCore ifc);
|
|||
Reg#(Maybe#(ArrayAddr)) inflight_A[2] <- mkCReg(2, tagged Invalid);
|
||||
Reg#(Maybe#(ArrayAddr)) inflight_B[2] <- mkCReg(2, tagged Invalid);
|
||||
|
||||
interface VRAMServer portA;
|
||||
interface Server portA;
|
||||
interface Put request;
|
||||
method Action put(VRAMRequest req) if (inflight_A[1] matches tagged Invalid);
|
||||
match {.array, .chip, .byteaddr} = split_addr(req.addr);
|
||||
|
@ -196,7 +210,7 @@ module mkVRAMCore(Integer num_kilobytes, VRAMCore ifc);
|
|||
endinterface
|
||||
endinterface
|
||||
|
||||
interface VRAMServer portB;
|
||||
interface Server portB;
|
||||
interface Put request;
|
||||
method Action put(VRAMRequest req) if (inflight_B[1] matches tagged Invalid);
|
||||
match {.array, .chip, .byteaddr} = split_addr(req.addr);
|
||||
|
|
Loading…
Reference in New Issue