/***************************************************************************** AvalonFIFOs =========== Simon Moore, Dec 2009 Dec 2009 - added multiported version Provides a set of FIFOs (specified by "num_fifos") and one Avalon Slave interface to access them. Data is tagged with one byte specified via register 1. The tag should be written before the data is written and it should be read after the data is read. Each FIFO is allocated 4 word addresses to access it. Processor memory map: read/write address meaning read 0 read from fifo write 0 write word to fifo read 1 bit 9 = overflow-output, bit 8 = underflow-input bits 7-0 = tag byte write 1 write tag byte read 2 number of waiting data items write 2 clear fifo + overflow and underflow bits read 3 number of empty fifo slots write 3 clear fifo + overflow and underflow bits Bug fixes: * in mkAvalonFIFOs_UGFIFOCount - moved fifo.clear from clear method to keep_count rule - the fifo is now cleared when the counter is cleared - seemed to be necessary to help the scheduler otherwise fifo.enq in do_enq never fired despite no_implicit_conditions *****************************************************************************/ package AvalonFIFOs; import GetPut::*; import Vector::*; import ClientServer::*; import Avalon2ClientServer::*; import FIFOF::*; import FIFOLevel::*; import FIFO::*; import Connectable::*; `define fifo_depth 32 // num fifos for single Avalon port version `define num_fifos 4 // num ports for multi-Avalon slave version `define num_ports 3 `define fifo_address_width 2 //`define replication_address_width Log(num_fifos) `define replication_address_width 2 //`define total_address_width (fifo_address_width+replication_address_width); `define total_address_width 4 (* always_enabled, always_ready *) interface AvalonFIFOs_UGFIFOCountIfc#( type element_type ); method Action enq ( element_type sendData ) ; method Action deq () ; method element_type first () ; method Bool notFull ; method Bool notEmpty ; method UInt#(32) count; //TODO: fix this hack... method Action clear; endinterface // like FIFOCountIfc but with ungarded enq and deq module mkAvalonFIFOs_UGFIFOCount#(Integer fifoDepth)(AvalonFIFOs_UGFIFOCountIfc#(element_type)) provisos (Bits#(element_type,element_type_width)); FIFOF#(element_type) fifo <- mkUGSizedFIFOF(fifoDepth); Wire#(Maybe#(element_type)) enq_dw <- mkDWire(tagged Invalid); PulseWire deq_pulse <- mkPulseWire; Reg#(UInt#(32)) counter <- mkReg(0); // hack - fix PulseWire count_down <- mkPulseWire; PulseWire count_up <- mkPulseWire; PulseWire count_zero <- mkPulseWire; Wire#(element_type) first_bw <- mkBypassWire; (* no_implicit_conditions *) rule do_first_bypass (True); // needed? first_bw <= fifo.notEmpty ? fifo.first : unpack(0); endrule (* no_implicit_conditions *) rule keep_count; if(count_zero) begin counter <= 0; fifo.clear; end else if(count_up && !count_down) counter<=counter+1; else if(!count_up && count_down) counter<=counter-1; endrule (* no_implicit_conditions *) rule do_enq (isValid(enq_dw) && fifo.notFull); fifo.enq(fromMaybe(?,enq_dw)); count_up.send; $display("%05d: %m - do_enq fired",$time); endrule rule do_enq_failed (isValid(enq_dw) && !fifo.notFull); $display("%05d: %m - WARNING: fifo enq when full",$time); endrule (* no_implicit_conditions *) rule do_deq (deq_pulse && fifo.notEmpty); fifo.deq; count_down.send; endrule rule do_deq_failed (deq_pulse && !fifo.notEmpty); $display("Warning: fifo deq when full"); endrule method Action enq ( element_type sendData ); enq_dw <= tagged Valid sendData; endmethod method Action deq (); deq_pulse.send; endmethod method element_type first (); return first_bw; endmethod method Bool notFull = fifo.notFull; method Bool notEmpty = fifo.notEmpty; method count; return extend(counter); endmethod method Action clear; count_zero.send; endmethod endmodule typedef UInt#(8) AvalonFIFOs_tagT; typedef struct { AvalonFIFOs_tagT tag; AvalonWordT data; } AvalonFIFOs_tagdataT deriving (Bits); typedef struct { UInt#(`replication_address_width) addr_top; UInt#(`fifo_address_width) addr_bot; } AvalonFIFOs_Addr_SeperatorT deriving (Bits); interface AvalonFIFOsIfc; interface AvalonSlaveIfc#(`total_address_width) avs; endinterface (* synthesize, reset_prefix = "csi_clockreset_reset_n", clock_prefix = "csi_clockreset_clk" *) (* doc="************** Simon's comments for Verilog code: **************" *) (* doc="Top level module for Avalon Bridge Example" *) (* doc="Port names should align with SOPC builder's new device requirements" *) (* doc="Need to add the $BLUESPECDIR/Verilog directory to the Libraries in Quartus" *) module mkAvalonFIFOs(AvalonFIFOsIfc); AvalonSlave2ClientIfc#(`total_address_width) slave <- mkAvalonSlave2Client; Vector#(`num_fifos,AvalonFIFOs_UGFIFOCountIfc#(AvalonFIFOs_tagdataT)) f <- replicateM(mkAvalonFIFOs_UGFIFOCount(`fifo_depth)); Vector#(`num_fifos,Reg#(AvalonFIFOs_tagT)) tag_in <- replicateM(mkReg(0)); Vector#(`num_fifos,Reg#(AvalonFIFOs_tagT)) tag_out <- replicateM(mkReg(0)); Vector#(`num_fifos,Reg#(Bool)) underflow <- replicateM(mkReg(False)); Vector#(`num_fifos,Reg#(Bool)) overflow <- replicateM(mkReg(False)); rule handle_requests; let req <- slave.client.request.get; ReturnedDataT rtn = tagged Invalid; AvalonFIFOs_Addr_SeperatorT a = unpack(pack(req.addr)); case(tuple2(a.addr_bot,req.rw)) tuple2(0, MemRead ) : if(f[a.addr_top].notEmpty) begin let d = f[a.addr_top].first; f[a.addr_top].deq; rtn = tagged Valid d.data; tag_in[a.addr_top] <= d.tag; end else begin rtn = tagged Valid 0; tag_in[a.addr_top] <= 0; underflow[a.addr_top] <= True; end tuple2(0, MemWrite) : if(f[a.addr_top].notFull) f[a.addr_top].enq(AvalonFIFOs_tagdataT{tag:tag_out[a.addr_top], data:req.data}); else overflow[a.addr_top] <= True; tuple2(1, MemRead ) : rtn = tagged Valid extend(unpack({pack(overflow[a.addr_top]),pack(underflow[a.addr_top]),pack(tag_in[a.addr_top])})); tuple2(1, MemWrite) : tag_out[a.addr_top] <= truncate(req.data); tuple2(2, MemRead ) : rtn = tagged Valid extend(f[a.addr_top].count); tuple2(2, MemWrite) : begin overflow[a.addr_top] <= False; underflow[a.addr_top] <= False; f[a.addr_top].clear; end tuple2(3, MemRead ) : rtn = tagged Valid extend(`fifo_depth-f[a.addr_top].count); tuple2(3, MemWrite) : begin overflow[a.addr_top] <= False; underflow[a.addr_top] <= False; f[a.addr_top].clear; end endcase slave.client.response.put(rtn); endrule interface avs=slave.avs; endmodule /***************************************************************************** AvalonFIFOMultiPort =================== Simon Moore, Dec 2009 Provides N Avalon Slaves and point-to-point FIFO communication between these ports (i.e. grows O(N^2)). It is constructed from N x AvalonFIFOOnePort Port P has N output ports and N input ports, i.e. there is one port which allows it to communicate with itself Use same address map as AvalonFIFOs. Processor memory map (word addressed): read/write address meaning read 0 read from fifo write 0 write word to fifo read 1 bit 9 = overflow-output, bit 8 = underflow-input bits 7-0 = tag byte write 1 write tag byte read 2 number of waiting data items write 2 clear fifo + overflow and underflow bits read 3 number of empty fifo slots write 3 clear fifo + overflow and underflow bits *****************************************************************************/ interface AvalonFIFOOnePortIfc; interface AvalonSlaveIfc#(`total_address_width) avs; interface Vector#(`num_ports,Client#(AvalonFIFOs_tagdataT,AvalonFIFOs_tagdataT)) chan; endinterface module mkAvalonFIFOOnePort(AvalonFIFOOnePortIfc); AvalonSlave2ClientIfc#(`total_address_width) slave <- mkAvalonSlave2Client; // TODO - use just one long fifo and token based flow control? // - N.B. require "number of empty fifo slots" for sender Vector#(`num_ports,AvalonFIFOs_UGFIFOCountIfc#(AvalonFIFOs_tagdataT)) fin <- replicateM(mkAvalonFIFOs_UGFIFOCount(`fifo_depth)); Vector#(`num_ports,AvalonFIFOs_UGFIFOCountIfc#(AvalonFIFOs_tagdataT)) fout <- replicateM(mkAvalonFIFOs_UGFIFOCount(`fifo_depth)); Vector#(`num_ports,Reg#(AvalonFIFOs_tagT)) tag_in <- replicateM(mkReg(0)); Vector#(`num_ports,Reg#(AvalonFIFOs_tagT)) tag_out <- replicateM(mkReg(0)); Vector#(`num_ports,Reg#(Bool)) underflow <- replicateM(mkReg(False)); Vector#(`num_ports,Reg#(Bool)) overflow <- replicateM(mkReg(False)); rule handle_requests; let req <- slave.client.request.get; ReturnedDataT rtn = tagged Invalid; AvalonFIFOs_Addr_SeperatorT a = unpack(pack(req.addr)); case(tuple2(a.addr_bot,req.rw)) tuple2(0, MemRead ) : if(fin[a.addr_top].notEmpty) begin let d = fin[a.addr_top].first; fin[a.addr_top].deq; rtn = tagged Valid d.data; tag_in[a.addr_top] <= d.tag; end else begin rtn = tagged Valid 0; tag_in[a.addr_top] <= 0; underflow[a.addr_top] <= True; end tuple2(0, MemWrite) : if(fout[a.addr_top].notFull) fout[a.addr_top].enq(AvalonFIFOs_tagdataT{tag:tag_out[a.addr_top], data:req.data}); else overflow[a.addr_top] <= True; tuple2(1, MemRead ) : rtn = tagged Valid extend(unpack({pack(overflow[a.addr_top]),pack(underflow[a.addr_top]),pack(tag_in[a.addr_top])})); tuple2(1, MemWrite) : tag_out[a.addr_top] <= truncate(req.data); tuple2(2, MemRead ) : rtn = tagged Valid extend(fin[a.addr_top].count); tuple2(2, MemWrite) : begin overflow[a.addr_top] <= False; underflow[a.addr_top] <= False; fout[a.addr_top].clear; end tuple2(3, MemRead ) : rtn = tagged Valid extend(`fifo_depth-fout[a.addr_top].count); tuple2(3, MemWrite) : begin overflow[a.addr_top] <= False; underflow[a.addr_top] <= False; fin[a.addr_top].clear; fout[a.addr_top].clear; end endcase slave.client.response.put(rtn); endrule // this is a bit of a hack to provide get and put interfaces to fifoin and fifoout Vector#(`num_ports,FIFO#(AvalonFIFOs_tagdataT)) bout <- replicateM(mkFIFO); Vector#(`num_ports,FIFO#(AvalonFIFOs_tagdataT)) bin <- replicateM(mkFIFO); for(Integer p=0; p<`num_ports; p=p+1) begin rule bin_to_fin (fin[p].notFull); fin[p].enq(bin[p].first); bin[p].deq; endrule rule fout_to_bout(fout[p].notEmpty); bout[p].enq(fout[p].first); fout[p].deq; endrule end Vector#(`num_ports,Client#(AvalonFIFOs_tagdataT,AvalonFIFOs_tagdataT)) c = newVector; for(Integer p=0; p<`num_ports; p=p+1) c[p] = Client{request: toGet(bout[p]), response: toPut(bin[p])}; interface chan=c; interface avs=slave.avs; endmodule interface AvalonFIFOMultiPortIfc; interface Vector#(`num_ports,AvalonSlaveIfc#(`total_address_width)) avs; endinterface (* synthesize, reset_prefix = "csi_clockreset_reset_n", clock_prefix = "csi_clockreset_clk" *) (* doc="************** Simon's comments for Verilog code: **************" *) (* doc="Top level module for Avalon Bridge Example" *) (* doc="Port names should align with SOPC builder's new device requirements" *) (* doc="Need to add the $BLUESPECDIR/Verilog directory to the Libraries in Quartus" *) module mkAvalonFIFOMultiPort(AvalonFIFOMultiPortIfc); Vector#(`num_ports,AvalonFIFOOnePortIfc) ports <- replicateM(mkAvalonFIFOOnePort); Vector#(`num_ports,AvalonSlaveIfc#(`total_address_width)) slaves; for(Integer src=0; src<`num_ports; src=src+1) for(Integer dest=0; dest<`num_ports; dest=dest+1) mkConnection(ports[src].chan[dest].request.get,ports[dest].chan[src].response.put); for(Integer j=0; j<`num_ports; j=j+1) slaves[j] = ports[j].avs; interface avs = slaves; endmodule endpackage