unroll for statements

unroll for statements

// Copyright (c) 2013 Quanta Research Cambridge, Inc.
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import FIFO::*;
import FIFOF::*;
import BRAMFIFO::*;
import GetPut::*;
import StmtFSM::*;
import ClientServer::*;

import PortalMemory::*;
import PortalRMemory::*;
import GetPutF::*;

import RingTypes::*;
import RingBuffer::*;
import CopyEngine::*;
import EchoEngine::*;
import NopEngine::*;

interface RingRequest;
   method Action set(Bit#(1) cmd, Bit#(2) regist, Bit#(32) addr);
   method Action get(Bit#(1) cmd, Bit#(2) regist);
   method Action hwenable(Bit#(1) en);
   method Action doCommandIndirect(Bit#(32) addr);
   method Action doCommandImmediate(Bit#(64) data);
endinterface

interface RingIndication;
   method Action setResult(Bit#(1) cmd, Bit#(2) regist, Bit#(32) addr);
   method Action getResult(Bit#(1) cmd, Bit#(2) regist, Bit#(32) addr);
   method Action completion(Bit#(32) command, Bit#(32) tag);
endinterface

module mkRingRequest#(RingIndication indication,
		      DMAReadServer#(64) dma_read_chan,
		      DMAWriteServer#(64) dma_write_chan,
		      DMAReadServer#(64) cmd_read_chan,
		      DMAWriteServer#(64) status_write_chan )(RingRequest);
   DMAReadBuffer#(64,8) copy_read_chan <- mkDMAReadBuffer();
   DMAWriteBuffer#(64,8) copy_write_chan <- mkDMAWriteBuffer();

   ServerF#(Bit#(64), Bit#(64)) copyEngine <- mkCopyEngine(dma_read_chan, dma_write_chan);
   ServerF#(Bit#(64), Bit#(64)) nopEngine <- mkNopEngine();
   ServerF#(Bit#(64), Bit#(64)) echoEngine <- mkEchoEngine();

   RingBuffer cmdRing <- mkRingBuffer;
   RingBuffer statusRing <- mkRingBuffer;
   Reg#(Bool) hwenabled <- mkReg(False);
   Reg#(Bool) cmdBusy <- mkReg(False);
   Reg#(UInt#(64)) cmd <- mkReg(0);

   let engineselect = cmd[63:56];
   function Server#(Bit#(64), Bit#(64)) cmdifc();
      if (engineselect == cmdNOP)
	 return nopEngine;
      else if (engineselect == cmdCOPY)
	 return copyEngine;
      else if (engineselect == cmdECHO)
	 return echoServer;
      else
	 return nopEngine;
   endfunction

   Stmt cmdFetch =   seq
      while (True) seq
	 while(!(hwenabled && cmdRing.notEmpty())) noAction;
	 cmd_read_chan.readReq.put(cmdRing.expBufferLast);
	 cmdRing.pop();
      endseq
		     endseq;

   let fn = cmd[63:56];

   Stmt cmdDispatch =
   seq
      while (True) seq
	 cmd <= cmd_read_chan.readData.get();
	 cmdifc.put(cmd);
	    cmdifc.put(cmd_read_chan.readData.get());
	    cmdifc.put(cmd_read_chan.readData.get());
	    cmdifc.put(cmd_read_chan.readData.get());
	    cmdifc.put(cmd_read_chan.readData.get());
	    cmdifc.put(cmd_read_chan.readData.get());
	    cmdifc.put(cmd_read_chan.readData.get());
	    cmdifc.put(cmd_read_chan.readData.get());
      endseq
   endseq;

   Stmt cmdCompletion =
   seq
      while(True) seq
	 while(!(hwenabled && statusRing.notFull())) noAction;
      endseq
   endseq;

   Stmt responseArbiter =
   seq
      while(True) seq
	 if (statusRing.notFull() && copyEngine.response.notEmpty())
	    for (ii <= 1; ii < 8; ii <= ii + 1)
	       action
		  status_write_chan.writeReq.put(statusRing.expBufferFirst);
		  status_write_chan.writeData.put(copyEngine.get());
		  statusRing.push(8);
	       endaction
	 if (statusRing.notFull() && echoEngine.response.notEmpty())
	    for (ii <= 1; ii < 8; ii <= ii + 1)
	       action
		  status_write_chan.writeReq.put(statusRing.expBufferFirst);
		  status_write_chan.writeData.put(echoEngine.get());
		  statusRing.push(8);
	       endaction
      endseq
   endseq;

   rule copyCompletion;
      let v <- ce.response.get();
      indication.ringIndication.completion(1, v);
   endrule

   mkAutoFSM (cmdFetch);
   mkAutoFSM (cmdDispatch);
   mkAutoFSM (cmdCompletion);

   interface RingRequest ringRequest;

      // to start a command, doCommand fires off a memory read to the
      // specified address. when it comes back, the doCommandRule will
      // handle it
      method Action doCommandIndirect(Bit#(DmaAddrSize) addr);
	 //cmd_read_chan.readReq.put(addr);
      endmethod

      method Action doCommandImmediate(Bit#(64) data);
      	 $display("doCommandImmediate %h", data);
      endmethod

      method Action set(Bit#(1) cmd, Bit#(2) regist, Bit#(32) addr);
	 if (cmd == 1)
	    cmdRing.set(regist, addr);
	 else
	    statusRing.set(regist, addr);
	 indication.ringIndication.setResult(cmd, regist, addr);
      endmethod

      method Action get(Bit#(1) cmd, Bit#(2) regist);
	 if (cmd == 1)
	    indication.ringIndication.getResult(1, regist, cmdRing.get(regist));
	 else
	    indication.ringIndication.getResult(0, regist,
	       statusRing.get(regist));
      endmethod

      method Action hwenable(Bit#(1) en);
	 hwenabled <= en == 1;
      endmethod

   endinterface

endmodule

// Copyright (c) 2013 Quanta Research Cambridge, Inc.
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import FIFO::*;
import FIFOF::*;
import BRAMFIFO::*;
import GetPut::*;
import StmtFSM::*;
import ClientServer::*;

import PortalMemory::*;
import PortalRMemory::*;
import GetPutF::*;

import RingTypes::*;
import RingBuffer::*;
import CopyEngine::*;
import EchoEngine::*;
import NopEngine::*;

interface RingRequest;
   method Action set(Bit#(1) cmd, Bit#(2) regist, Bit#(32) addr);
   method Action get(Bit#(1) cmd, Bit#(2) regist);
   method Action hwenable(Bit#(1) en);
   method Action doCommandIndirect(Bit#(32) addr);
   method Action doCommandImmediate(Bit#(64) data);
endinterface

interface RingIndication;
   method Action setResult(Bit#(1) cmd, Bit#(2) regist, Bit#(32) addr);
   method Action getResult(Bit#(1) cmd, Bit#(2) regist, Bit#(32) addr);
   method Action completion(Bit#(32) command, Bit#(32) tag);
endinterface

module mkRingRequest#(RingIndication indication,
		      DMAReadServer#(64) dma_read_chan,
		      DMAWriteServer#(64) dma_write_chan,
		      DMAReadServer#(64) cmd_read_chan,
		      DMAWriteServer#(64) status_write_chan )(RingRequest);
   DMAReadBuffer#(64,8) copy_read_chan <- mkDMAReadBuffer();
   DMAWriteBuffer#(64,8) copy_write_chan <- mkDMAWriteBuffer();

   ServerF#(Bit#(64), Bit#(64)) copyEngine <- mkCopyEngine(dma_read_chan, dma_write_chan);
   ServerF#(Bit#(64), Bit#(64)) nopEngine <- mkNopEngine();
   ServerF#(Bit#(64), Bit#(64)) echoEngine <- mkEchoEngine();

   RingBuffer cmdRing <- mkRingBuffer;
   RingBuffer statusRing <- mkRingBuffer;
   Reg#(Bool) hwenabled <- mkReg(False);
   Reg#(Bool) cmdBusy <- mkReg(False);
   Reg#(UInt#(64)) cmd <- mkReg(0);

   let engineselect = cmd[63:56];
   function Server#(Bit#(64), Bit#(64)) cmdifc();
      if (engineselect == cmdNOP)
	 return nopEngine;
      else if (engineselect == cmdCOPY)
	 return copyEngine;
      else if (engineselect == cmdECHO)
	 return echoServer;
      else
	 return nopEngine;
   endfunction

   Stmt cmdFetch =   seq
      while (True) seq
	 while(!(hwenabled && cmdRing.notEmpty())) noAction;
	 cmd_read_chan.readReq.put(cmdRing.expBufferLast);
	 cmdRing.pop();
      endseq
		     endseq;

   let fn = cmd[63:56];

   Stmt cmdDispatch =
   seq
      while (True) seq
	 cmd <= cmd_read_chan.readData.get();
	 cmdifc.put(cmd);
	 for (ii <= 1; ii < 8; ii <= ii + 1)
	    cmdifc.put(cmd_read_chan.readData.get());
      endseq
   endseq;

   Stmt cmdCompletion =
   seq
      while(True) seq
	 while(!(hwenabled && statusRing.notFull())) noAction;
      endseq
   endseq;

   Stmt responseArbiter =
   seq
      while(True) seq
	 if (statusRing.notFull() && copyEngine.response.notEmpty())
	    for (ii <= 1; ii < 8; ii <= ii + 1)
	       action
		  status_write_chan.writeReq.put(statusRing.expBufferFirst);
		  status_write_chan.writeData.put(copyEngine.get());
		  statusRing.push(8);
	       endaction
	 if (statusRing.notFull() && echoEngine.response.notEmpty())
	    for (ii <= 1; ii < 8; ii <= ii + 1)
	       action
		  status_write_chan.writeReq.put(statusRing.expBufferFirst);
		  status_write_chan.writeData.put(echoEngine.get());
		  statusRing.push(8);
	       endaction
      endseq
   endseq;

   rule copyCompletion;
      let v <- ce.response.get();
      indication.ringIndication.completion(1, v);
   endrule

   mkAutoFSM (cmdFetch);
   mkAutoFSM (cmdDispatch);
   mkAutoFSM (cmdCompletion);

   interface RingRequest ringRequest;

      // to start a command, doCommand fires off a memory read to the
      // specified address. when it comes back, the doCommandRule will
      // handle it
      method Action doCommandIndirect(Bit#(DmaAddrSize) addr);
	 //cmd_read_chan.readReq.put(addr);
      endmethod

      method Action doCommandImmediate(Bit#(64) data);
      	 $display("doCommandImmediate %h", data);
      endmethod

      method Action set(Bit#(1) cmd, Bit#(2) regist, Bit#(32) addr);
	 if (cmd == 1)
	    cmdRing.set(regist, addr);
	 else
	    statusRing.set(regist, addr);
	 indication.ringIndication.setResult(cmd, regist, addr);
      endmethod

      method Action get(Bit#(1) cmd, Bit#(2) regist);
	 if (cmd == 1)
	    indication.ringIndication.getResult(1, regist, cmdRing.get(regist));
	 else
	    indication.ringIndication.getResult(0, regist,
	       statusRing.get(regist));
      endmethod

      method Action hwenable(Bit#(1) en);
	 hwenabled <= en == 1;
      endmethod

   endinterface

endmodule


pull across changes from examples/memread into examples/memread_simple

// Copyright (c) 2013 Quanta Research Cambridge, Inc.

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import FIFO::*;
import FIFOF::*;
import Vector::*;
import GetPut::*;
import ClientServer::*;
import Pipe::*;
import MemTypes::*;
import MemreadEngine::*;
import Pipe::*;
import HostInterface::*; // for DataBusWidth

interface ReadTestRequest;
   method Action startRead(Bit#(32) pointer, Bit#(32) numBytes, Bit#(32) burstLenInBytes, Bit#(32) iterCnt);
endinterface

interface ReadTest;
   interface ReadTestRequest request;
   interface Vector#(1,MemReadClient#(DataBusWidth)) dmaClient;
endinterface

interface ReadTestIndication;
   method Action readDone(Bit#(32) mismatchCount);
endinterface

module mkReadTest#(ReadTestIndication indication) (ReadTest);
   Reg#(SGLId)   pointer <- mkReg(0);
   Reg#(Bit#(32))       numBytes <- mkReg(0);
   Reg#(Bit#(BurstLenSize)) burstLenInBytes <- mkReg(0);
   Reg#(Bit#(32))  itersToFinish <- mkReg(0);
   Reg#(Bit#(32))   itersToStart <- mkReg(0);
   Reg#(Bit#(32))      bytesRead <- mkReg(0);
   Reg#(Bit#(32)) mismatchCounts <- mkReg(0);
   MemreadEngine#(DataBusWidth,2,1) re <- mkMemreadEngine;

   rule start (itersToStart > 0);
      re.readServers[0].request.put(MemengineCmd{sglId:pointer, base:0, len:numBytes, burstLen:burstLenInBytes});
      itersToStart <= itersToStart-1;
   endrule

   function Bit#(32) expectedVal(Integer i); return (bytesRead/4)+fromInteger(i); endfunction
   rule check;
      let v <- toGet(re.readServers[0].data).get;
      if (v.data != pack(genWith(expectedVal)))
         mismatchCounts <= mismatchCounts + 1;
      let new_bytesRead = bytesRead + fromInteger(valueOf(DataBusWidth))/8;
      if (v.last) begin
	 new_bytesRead = 0;
         if (itersToFinish == 1)
	    indication.readDone(mismatchCounts);
         itersToFinish <= itersToFinish - 1;
      end
      bytesRead <= new_bytesRead;
   endrule

   interface dmaClient = cons(re.dmaClient, nil);
   interface ReadTestRequest request;
      method Action startRead(Bit#(32) rp, Bit#(32) nb, Bit#(32) bl, Bit#(32) ic) if (itersToFinish == 0);
	 pointer <= rp;
	 numBytes <= nb;
	 burstLenInBytes  <= truncate(bl);
	 itersToFinish <= ic;
	 itersToStart <= ic;
	 mismatchCounts <= 0;
	 bytesRead <= 0;
      endmethod
   endinterface
endmodule

// Copyright (c) 2013 Quanta Research Cambridge, Inc.

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import FIFO::*;
import FIFOF::*;
import Vector::*;
import GetPut::*;
import ClientServer::*;
import Pipe::*;
import MemTypes::*;
import MemreadEngine::*;
import Pipe::*;
import HostInterface::*; // for DataBusWidth

interface ReadTestRequest;
   method Action startRead(Bit#(32) pointer, Bit#(32) numBytes, Bit#(32) burstLenInBytes, Bit#(32) iterCnt);
endinterface

interface ReadTest;
   interface ReadTestRequest request;
   interface Vector#(1,MemReadClient#(DataBusWidth)) dmaClient;
endinterface

interface ReadTestIndication;
   method Action readDone(Bit#(32) mismatchCount);
endinterface

module mkReadTest#(ReadTestIndication indication) (ReadTest);
   Reg#(SGLId)   pointer <- mkReg(0);
   Reg#(Bit#(32))       numBytes <- mkReg(0);
   Reg#(Bit#(BurstLenSize)) burstLenInBytes <- mkReg(0);
   Reg#(Bit#(32))  itersToFinish <- mkReg(0);
   Reg#(Bit#(32))   itersToStart <- mkReg(0);
   Reg#(Bit#(32))        srcGens <- mkReg(0);
   Reg#(Bit#(32)) mismatchCounts <- mkReg(0);
   MemreadEngine#(DataBusWidth,2,1) re <- mkMemreadEngine;

   rule start (itersToStart > 0);
      re.readServers[0].request.put(MemengineCmd{sglId:pointer, base:0, len:numBytes, burstLen:burstLenInBytes});
      itersToStart <= itersToStart-1;
   endrule

   function Bit#(32) expectedVal(Integer i); return srcGens+fromInteger(i); endfunction
   rule check;
      let v <- toGet(re.readServers[0].data).get;
      let expectedV = pack(genWith(expectedVal));
      let misMatch = v.data != expectedV;
      mismatchCounts <= mismatchCounts + (misMatch ? 1 : 0);
      let new_srcGens = srcGens+2;
      if (v.last) begin
	 new_srcGens = 0;
         if (itersToFinish == 1) begin
	    indication.readDone(mismatchCounts);
         end
         itersToFinish <= itersToFinish - 1;
      end
      srcGens <= new_srcGens;
   endrule

   interface dmaClient = cons(re.dmaClient, nil);
   interface ReadTestRequest request;
      method Action startRead(Bit#(32) rp, Bit#(32) nb, Bit#(32) bl, Bit#(32) ic) if (itersToStart == 0 && itersToFinish == 0);
	 pointer <= rp;
	 numBytes <= nb;
	 burstLenInBytes  <= truncate(bl);
	 itersToFinish <= ic;
	 itersToStart <= ic;
	 mismatchCounts <= 0;
	 srcGens <= 0;
      endmethod
   endinterface
endmodule


In Branch Predictor, added 2-bit hysteresis FSM for BTB entries

// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved

package Branch_Predictor;

// ================================================================
// Branch Predictor for RISC-V CPU
//
// BTB (Branch Target Buffer) with 2-bit FSM for hysteresis.
//         Direct-mapped.
// RAS (Return-Address Stack)

// ================================================================
// Exports

export Branch_Predictor_IFC (..), mkBranch_Predictor;

// ================================================================
// BSV library imports

import Vector    :: *;
import RegFile   :: *;
import BRAMCore  :: *;
import FIFOF     :: *;

// ----------------
// BSV additional libs

import Cur_Cycle  :: *;

// ================================================================
// Project imports

import ISA_Decls   :: *;
import CPU_Globals :: *;

// ================================================================

interface Branch_Predictor_IFC;
   method Action  reset;

   // ----------------
   // Request prediction for given pc; available on 'predict_rsp' on next cycle

   method Action  predict_req (WordXL pc);

   // ----------------
   // Response for 'predict_req' from an earlier cycle.
   // Args describe current instruction just now fetched in Fetch stage,
   // and are used to choose RAS actions if any, and size of
   // fall-through PC if no prediction.

   (* always_ready *)
   method WordXL  predict_rsp (Bool is_i32_not_i16, Instr instr);

   // ----------------
   // Train BTB and RAS.
   // First 3 args are from current fetch, to train RAS.
   // cf_info arg is from Exec stage (downpipe), from an earlier (older) instruction.

   method Action bp_train (WordXL   pc,
			   Bool     is_i32_not_i16,
			   Instr    instr,
			   CF_Info  cf_info);
endinterface

// ================================================================
// BTB parameters and functions.
// Ignore PCs[0] since PCs are at least 2-byte aligned (word-aligned).

typedef  512  BTB_Num_Sets;
Integer  btb_num_sets = valueOf (BTB_Num_Sets);

// Split a PC into a btb index and a btb tag
// Bit #(XLEN) pc = { tag, index, 1'b0 }

typedef TLog #(BTB_Num_Sets)  BTB_Index_Sz;
Integer btb_index_sz  = valueOf (BTB_Index_Sz);

typedef Bit #(BTB_Index_Sz)                           BTB_Index;

typedef Bit #(TSub #(TSub #(XLEN, BTB_Index_Sz), 1))  BTB_Tag;

// ----------------
// Pick out PC bits to use as index into BTB.

function BTB_Index  fn_pc_to_index (WordXL pc);
   return pc [btb_index_sz : 1];
endfunction

function BTB_Tag  fn_pc_to_tag (WordXL pc);
   return pc [(valueOf (XLEN) - 1) : btb_index_sz + 1];
endfunction

function Word_Addr  fn_pc_to_word_addr (WordXL pc);
   return pc [(valueOf (XLEN) - 1) : 1];
endfunction

// ----------------
// BTB entries

typedef Bit #(TSub #(XLEN, 1)) Word_Addr;

typedef struct {
   Bool       valid;
   BTB_Tag    pc_tag;
   Word_Addr  predicted_pc;
   } BTB_Entry
deriving (Bits, FShow);

// ================================================================
// RAS constants and functions

typedef  16  RAS_Capacity;

// ----------------
// Compute actions on RAS.
// Results are do_pop, do_push, and ret_PC (for push)

function Tuple3 #(Bool, Bool, WordXL) fn_ras_actions (WordXL  pc,
						      Bool    is_i32_not_i16,
						      Instr   instr);
   // Classify instr, for 32-bit instuctions
   WordXL ret_pc        = pc + 4;
   Bool   rd_is_link    = fn_reg_is_link (instr_rd  (instr));
   Bool   rs1_is_link   = fn_reg_is_link (instr_rs1 (instr));
   Bool   rs1_eq_rd     = (instr_rd  (instr) == instr_rs1 (instr));
   Bool   instr_is_JAL  = (instr_opcode (instr) == op_JAL);
   Bool   instr_is_JALR = (instr_opcode (instr) == op_JALR);

`ifdef ISA_C
   // Classify instr, for 16-bit instructions ('C' extension)
   if (! (is_i32_not_i16)) begin
      ret_pc = pc + 2;
      let funct3 = instr [15:13];
      let funct4 = instr [15:12];
      let rs1    = instr [11:7];
      let rs2    = instr [6:2];
      let op     = instr [1:0];
      if ((funct3 == funct3_C_J) && (op == opcode_C1)) begin
	 // C.J = JAL x0, offset
	 rd_is_link    = False;    // rd is x0
	 instr_is_JAL  = True;
	 instr_is_JALR = False;
      end
      else if ((funct3 == funct3_C_JAL) && (op == opcode_C1) && (xlen == 32)) begin
	 // C.JAL = JAL x1, offset
	 rd_is_link    = True;    // rd is x1
	 instr_is_JAL  = True;
	 instr_is_JALR = False;
      end
      else if ((funct4 == funct4_C_JR) && (rs1 != 0) && (rs2 == 0) && (op == opcode_C2)) begin
	 // C.JR = JALR x0, 0(rs1)
	 rd_is_link    = False;    // rd is x0
	 rs1_is_link   = fn_reg_is_link (rs1);
	 rs1_eq_rd     = False;
	 instr_is_JAL  = False;
	 instr_is_JALR = True;
      end
      else if ((funct4 == funct4_C_JALR) && (rs1 != 0) && (rs2 == 0) && (op == opcode_C2)) begin
	 // C.JALR = JALR x1, 0(rs1)
	 rd_is_link    = True;    // rd is x1
	 rs1_is_link   = fn_reg_is_link (rs1);
	 rs1_eq_rd     = (rs1 == x1);
	 instr_is_JAL  = False;
	 instr_is_JALR = True;
      end
   end
`endif

   // Compute RAS actions based on classification.
   // (cf. RISC-V Unprivileged ISA Manual, Sec 2.5, Control Transfer Instructions/ Unconditional Jumps)
   Bool do_pop  = False;
   Bool do_push = False;
   if (instr_is_JAL && rd_is_link)
      do_push = True;
   else if (instr_is_JALR) begin
      if ((! rd_is_link) && rs1_is_link)
	 do_pop = True;
      else if (rd_is_link && (! rs1_is_link))
	 do_push = True;
      else if (rd_is_link && rs1_is_link) begin
	 do_push = True;
	 if (! rs1_eq_rd)
	    do_pop  = True;
      end
   end
   return tuple3 (do_pop, do_push, ret_pc);
endfunction

// ================================================================

(* synthesize *)
module mkBranch_Predictor (Branch_Predictor_IFC);

   Integer verbosity = 0;
   WordXL  bogus_PC  = '1;

   // Branch Target Table (BTB)
   Bool bram_has_output_reg = False;
   BRAM_DUAL_PORT #(BTB_Index, BTB_Entry) btb_bramcore2 <- mkBRAMCore2 (btb_num_sets,
									bram_has_output_reg);

   // 2-bit FSMs for BTB hysteresis.
   //     00 = strong not-taken
   //     01 = weak   not-taken
   //     10 = weak   taken
   //     11 = strong taken
   // This is a regfile separate from btb_bramcore2 to allow combinational lookup,
   // to enable read-modify-write in a cycle.
   RegFile #(BTB_Index, Bit #(2)) rf_btb_fsms <- mkRegFileFull;

   // Return Address Stack (RAS). Top of stack is always at [0]. Push/pop by shifting.
   Reg #(Vector #(RAS_Capacity, WordXL)) rg_ras  <- mkReg (replicate (bogus_PC));

   // This reg holds the PC being predicted (currently probing the btb)
   Reg #(WordXL)  rg_pc <- mkRegU;

   // For reset
   Reg #(Bool)       rg_resetting <- mkReg (True);
   Reg #(BTB_Index)  rg_index     <- mkReg (0);

   // ----------------------------------------------------------------
   // BEHAVIOR

   // Reset loop: invalidate each BTB entry and set each hysteresis FSM to 0.

   rule rl_reset (rg_resetting);
      let btb_entry = BTB_Entry {valid: False,
				 pc_tag: 0,
				 predicted_pc: 0};
      Bool op_write_not_read = True;
      btb_bramcore2.b.put (op_write_not_read, rg_index, btb_entry);
      rf_btb_fsms.upd (rg_index, 2'b00);    // strong not-taken

      if (rg_index == '1) begin
	 rg_resetting <= False;
	 if (verbosity > 1)
	    $display ("%0d: %m.rl_reset: reset complete", cur_cycle);
      end
      rg_index <= rg_index + 1;
   endrule

   // ----------------------------------------------------------------
   // INTERFACE

   method Action reset;
      rg_index     <= 0;
      rg_resetting <= True;
   endmethod

   // ----------------
   // Request prediction for given pc; available on 'predict_rsp' on next cycle

   method Action  predict_req (WordXL pc) if (! rg_resetting);
      if (verbosity > 1)
	 $display ("%0d: %m.predict_req (pc 0x%0h)", cur_cycle, pc);

      let index_a = fn_pc_to_index (pc);
      Bool op_write_not_read = False;
      btb_bramcore2.a.put (op_write_not_read, index_a, ?);
      rg_pc <= pc;
   endmethod

   // ----------------
   // Response for 'predict_req' from an earlier cycle.
   // Args describe current instruction just now fetched in Fetch stage,
   // and are used to choose RAS actions if any, and size of
   // fall-through PC if no prediction.

   method WordXL  predict_rsp (Bool is_i32_not_i16, Instr instr);
      WordXL pred_pc   = bogus_PC;    // default: no prediction, invalid PC
      let    btb_entry = btb_bramcore2.a.read;

      // Check if prediction is from RAS pop (JALR returns)
      match { .do_pop, .*, .* } = fn_ras_actions (bogus_PC, is_i32_not_i16, instr);
      if (do_pop)
	 pred_pc = rg_ras [0];    // Top of RAS (will be popped in bp_train())

      // Look for BTB hit if no result from RAS
      if (   (pred_pc == bogus_PC)
	  && (btb_entry.valid)
	  && (btb_entry.pc_tag == fn_pc_to_tag (rg_pc))) begin
	 pred_pc = { btb_entry.predicted_pc, 1'b0 };
      end

      // Default prediction: fallthrough
      if (pred_pc == bogus_PC)
	 pred_pc = rg_pc + (is_i32_not_i16 ? 4 : 2);

      return pred_pc;
   endmethod

   // ----------------
   // Train BTB and RAS.
   // First 3 args are from current fetch, to train RAS.
   // cf_info arg is from Exec stage (downpipe), from an earlier (older) instruction.

   method Action bp_train (WordXL   pc,
			   Bool     is_i32_not_i16,
			   Instr    instr,
			   CF_Info  cf_info);
      if (verbosity > 2)
	 $display ("%0d: %m.bp_train: pc %0x  is_i32 %0d  instr %0x ",
		   cur_cycle, pc, is_i32_not_i16, instr, fshow (cf_info));

      // ----------------
      // RAS training using (pc, is_i32_not_i16, instr) which are from currently fetched instr
      // Top-of-stack is at ras [0]

      match { .do_pop, .do_push, .ret_pc } = fn_ras_actions (pc, is_i32_not_i16, instr);
      let ras_val  = rg_ras;
      if (do_pop)  ras_val = shiftInAtN (ras_val, bogus_PC);
      if (do_push) ras_val = shiftInAt0 (ras_val, ret_pc);
      rg_ras       <= ras_val;

      // ----------------
      // BTB training using cf_info which is feedback from downpipe
      // exec stage (several instructions earlier)

      WordXL    pred_PC = bogus_PC;
      BTB_Index index_b = fn_pc_to_index (cf_info.from_PC);
      Bit #(2)  fsm_val = rf_btb_fsms.sub (index_b);

      if (cf_info.cf_op == CF_BR) begin
	 if (cf_info.taken)
	    case (fsm_val)
	       2'b00: begin fsm_val = 2'b01; pred_PC  = cf_info.fallthru_PC; end
	       2'b01: begin fsm_val = 2'b10; pred_PC  = cf_info.taken_PC;    end
	       2'b10: begin fsm_val = 2'b11; pred_PC  = cf_info.taken_PC;    end
	       2'b11: begin fsm_val = 2'b11; pred_PC  = cf_info.taken_PC;    end
	    endcase
	 else
	    case (fsm_val)
	       2'b00: begin fsm_val = 2'b00; pred_PC  = cf_info.fallthru_PC; end
	       2'b01: begin fsm_val = 2'b00; pred_PC  = cf_info.fallthru_PC; end
	       2'b10: begin fsm_val = 2'b01; pred_PC  = cf_info.fallthru_PC; end
	       2'b11: begin fsm_val = 2'b10; pred_PC  = cf_info.taken_PC;    end
	    endcase
      end
      else if ((cf_info.cf_op == CF_JAL) || (cf_info.cf_op == CF_JALR)) begin
	 fsm_val = 2'b11;    // strong taken
	 pred_PC = cf_info.taken_PC;
      end
      if (pred_PC != bogus_PC) begin
	 rf_btb_fsms.upd (index_b, fsm_val);
	 let btb_entry = BTB_Entry {valid:        True,
				    pc_tag:       fn_pc_to_tag (cf_info.from_PC),
				    predicted_pc: fn_pc_to_word_addr (pred_PC) };
	 Bool op_write_not_read = True;
	 btb_bramcore2.b.put (op_write_not_read, index_b, btb_entry);
	 if (verbosity > 2)
	    $display ("    insert prediction [0x%0h] <= (%0h -> %0h)",
		      index_b, cf_info.from_PC, pred_PC);
      end
   endmethod

endmodule

// ================================================================

endpackage

// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved

package Branch_Predictor;

// ================================================================
// Branch Predictor for RISC-V CPU
//
// This is implemented as a simple direct-mapped cache.

// The 'insert' method overwrites the previous entry in the cache
//     (there is no 'writeback' of the victim)

// The 'predict' method requests a prediction.
// The 'prediction' method returns (and consumes) the response
// and is available from the next cycle onwards.
//    On a 'hit', returns 'predicted_pc'
//    On a 'miss', returns 'pc+4' (there is no 'cache refill')

// If there was no previous 'predict' request, then 'prediction'
// returns an invalid PC ('1') (no prediction).

// ================================================================
// Exports

export Branch_Predictor_IFC (..), mkBranch_Predictor;

// ================================================================
// BSV library imports

import Vector    :: *;
import BRAMCore  :: *;
import FIFOF     :: *;

// ----------------
// BSV additional libs

import Cur_Cycle  :: *;

// ================================================================
// Project imports

import ISA_Decls   :: *;
import CPU_Globals :: *;

// ================================================================

interface Branch_Predictor_IFC;
   method Action  reset;

   method Action  predict_req (WordXL pc);

   (* always_ready *)
   method WordXL  predict_rsp (Bool is_i32_not_i16, Instr instr);

   method Action bp_train (WordXL   pc,
			   Bool     is_i32_not_i16,
			   Instr    instr,
			   CF_Info  cf_info);
endinterface

// ================================================================
// Cache parameters.
// PCs are at least 2-byte aligned, so we can ignore the lsb (always 0)

typedef  512  Cache_Num_Sets;
Integer  cache_num_sets = valueOf (Cache_Num_Sets);

// Splitting a PC into a cache index and a cache tag
// Bit #(XLEN) pc = { tag, index, 1'b0 }

typedef TLog #(Cache_Num_Sets)  Cache_Index_sz;
Integer cache_index_sz  = valueOf (Cache_Index_sz);

typedef Bit #(Cache_Index_sz)                           Cache_Index;

typedef Bit #(TSub #(TSub #(XLEN, Cache_Index_sz), 1))  Cache_Tag;

typedef Bit #(TSub #(XLEN, 1)) WordXL_Addr;

typedef struct {
   Bool         valid;
   Cache_Tag    pc_tag;
   WordXL_Addr  predicted_pc;
   } Cache_Entry
deriving (Bits, FShow);

typedef  16  RAS_Capacity;

// ================================================================
// Compute actions on RAS

function Tuple3 #(Bool, Bool, WordXL) fn_ras_actions (WordXL  pc,
						      Bool    is_i32_not_i16,
						      Instr   instr);
   // For 32-bit instuctions
   WordXL ret_pc      = pc + 4;
   Bool rd_is_link    = fn_reg_is_link (instr_rd  (instr));
   Bool rs1_is_link   = fn_reg_is_link (instr_rs1 (instr));
   Bool rs1_eq_rd     = (instr_rd  (instr) == instr_rs1 (instr));
   Bool instr_is_JAL  = (instr_opcode (instr) == op_JAL);
   Bool instr_is_JALR = (instr_opcode (instr) == op_JALR);

`ifdef ISA_C
   // For 16-bit instructions ('C' extension)
   if (! (is_i32_not_i16)) begin
      ret_pc = pc + 2;
      let rs1 = instr [11:7];
      let rs2 = instr [6:2];
      if ((instr [15:13] == funct3_C_J) && (instr [1:0] == opcode_C1)) begin
	 // C.J = JAL x0, offset
	 rd_is_link    = False;    // rd is x0
	 instr_is_JAL  = True;
	 instr_is_JALR = False;
      end
      else if ((instr [15:13] == funct3_C_JAL) && (instr [1:0] == opcode_C1) && (xlen == 32)) begin
	 // C.JAL = JAL x1, offset
	 rd_is_link    = True;    // rd is x1
	 instr_is_JAL  = True;
	 instr_is_JALR = False;
      end
      else if (   (instr [15:12] == funct4_C_JR)
	       && (instr [11:7] != 0)                // rs1
	       && (instr [6:2] == 0)
	       && (instr [1:0] == opcode_C2)) begin
	 // C.JR = JALR x0, 0(rs1)
	 rd_is_link    = False;    // rd is x0
	 rs1_is_link   = fn_reg_is_link (instr [11:7]);
	 rs1_eq_rd     = False;
	 instr_is_JAL  = False;
	 instr_is_JALR = True;
      end
      else if (   (instr [15:12] == funct4_C_JALR)
	       && (instr [11:7] != 0)                // rs1
	       && (instr [6:2] == 0)
	       && (instr [1:0] == opcode_C2)) begin
	 // C.JALR = JALR x1, 0(rs1)
	 rd_is_link    = True;    // rd is x1
	 rs1_is_link   = fn_reg_is_link (instr [11:7]);
	 rs1_eq_rd     = (instr [11:7] == x1);
	 instr_is_JAL  = False;
	 instr_is_JALR = True;
      end
   end
`endif

   Bool do_pop  = False;
   Bool do_push = False;
   if (instr_is_JAL && rd_is_link)
      do_push = True;
   else if (instr_is_JALR) begin
      if ((! rd_is_link) && rs1_is_link)
	 do_pop = True;
      else if (rd_is_link && (! rs1_is_link))
	 do_push = True;
      else if (rd_is_link && rs1_is_link) begin
	 do_push = True;
	 if (! rs1_eq_rd)
	    do_pop  = True;
      end
   end
   return tuple3 (do_pop, do_push, ret_pc);
endfunction

// ================================================================

(* synthesize *)
module mkBranch_Predictor (Branch_Predictor_IFC);

   Integer verbosity = 0;
   WordXL  bogus_PC  = '1;

   // Branch Target Table (BTB)
   BRAM_DUAL_PORT #(Cache_Index, Cache_Entry) btb_bramcore2
      <- mkBRAMCore2 (cache_num_sets, /* hasOutputRegister */ False);

   // Return Address Stack (RAS). Top of stack is at [0].
   Reg #(Vector #(RAS_Capacity, WordXL)) rg_ras       <- mkReg (replicate (bogus_PC));

   // This FIFO holds the PC being predicted (currently probing the cache)
   Reg #(WordXL)  rg_pc <- mkRegU;

   // For reset
   Reg #(Bool)         rg_resetting <- mkReg (True);
   Reg #(Cache_Index)  rg_index     <- mkReg (0);

   // ----------------------------------------------------------------
   // Pick out bits from the PC to use as index into the table
   // Ignore bottom 2 lsbs since addresses are 4-byte-aligned.

   function Cache_Index  fn_pc_to_index (WordXL pc);
      return pc [cache_index_sz : 1];
   endfunction

   function Cache_Tag  fn_pc_to_tag (WordXL pc);
      return pc [(valueOf (XLEN) - 1) : cache_index_sz + 1];
   endfunction

   function WordXL_Addr  fn_pc_to_word_addr (WordXL pc);
      return pc [(valueOf (XLEN) - 1) : 1];
   endfunction

   // ----------------------------------------------------------------
   // BEHAVIOR

   rule rl_reset (rg_resetting);
      let data = Cache_Entry {valid: False,
			      pc_tag: 0,
			      predicted_pc: 0};
      btb_bramcore2.b.put (/* write */ True, rg_index, data);
      if (rg_index == '1) begin
	 rg_resetting <= False;
	 if (verbosity > 1)
	    $display ("%0d: %m.rl_reset: reset complete", cur_cycle);
      end
      rg_index <= rg_index + 1;
   endrule

   // ----------------------------------------------------------------
   // INTERFACE

   method Action reset;
      rg_index     <= 0;
      rg_resetting <= True;
   endmethod

   method Action  predict_req (WordXL pc) if (! rg_resetting);
      if (verbosity > 1)
	 $display ("%0d: %m.predict_req (pc 0x%0h)", cur_cycle, pc);

      let index_a = fn_pc_to_index (pc);
      Bool op_write_not_read = False;
      btb_bramcore2.a.put (op_write_not_read, index_a, ?);
      rg_pc <= pc;
   endmethod

   method WordXL  predict_rsp (Bool is_i32_not_i16, Instr instr);
      WordXL pred_pc = bogus_PC;    // default: no prediction, invalid PC

      let cache_entry = btb_bramcore2.a.read;
      match { .do_pop, .*, .* } = fn_ras_actions (bogus_PC, is_i32_not_i16, instr);

      if (do_pop) begin
	 pred_pc = rg_ras [0];    // Top of RAS (will be popped in bp_train())
      end

      // Lookup BTB if no result from RAS
      if (   (pred_pc == bogus_PC)
	  && (cache_entry.valid)
	  && (cache_entry.pc_tag == fn_pc_to_tag (rg_pc))) begin
	 // BTB Hit
	 pred_pc = { cache_entry.predicted_pc, 1'b0 };
      end

      // Default
      if (pred_pc == bogus_PC)
	 pred_pc = rg_pc + (is_i32_not_i16 ? 4 : 2);

      return pred_pc;
   endmethod

   method Action bp_train (WordXL   pc,
			   Bool     is_i32_not_i16,
			   Instr    instr,
			   CF_Info  cf_info);
      if (verbosity > 2)
	 $display ("%0d: %m.bp_train: pc %0x  is_i32 %0d  instr %0x ",
		   cur_cycle, pc, is_i32_not_i16, instr, fshow (cf_info));

      // ----------------
      // RAS training using (pc, is_i32_not_i16, instr) which are from currently fetched instr
      // Top-of-stack is at ras [0]

      match { .do_pop, .do_push, .ret_pc } = fn_ras_actions (pc, is_i32_not_i16, instr);
      let ras_val   = rg_ras;
      if (do_pop)  ras_val = shiftInAtN (ras_val, bogus_PC);
      if (do_push) ras_val = shiftInAt0 (ras_val, ret_pc);
      rg_ras       <= ras_val;

      // ----------------
      // BTB training using cf_info which is feedback from downpipe
      // exec stage (several instructions earlier)

      WordXL pred_PC = bogus_PC;

      if (cf_info.cf_op == CF_BR)
	 pred_PC = (cf_info.taken ? cf_info.taken_PC : cf_info.fallthru_PC);

      else if ((cf_info.cf_op == CF_JAL) || (cf_info.cf_op == CF_JALR))
	 pred_PC = cf_info.taken_PC;

      if (pred_PC != bogus_PC) begin
	 Cache_Index index_b = fn_pc_to_index (cf_info.from_PC);
	 let data = Cache_Entry {valid:        True,
				 pc_tag:       fn_pc_to_tag (cf_info.from_PC),
				 predicted_pc: fn_pc_to_word_addr (pred_PC) };
         Bool op_write_not_read = True;
	 btb_bramcore2.b.put (op_write_not_read, index_b, data);
	 if (verbosity > 2)
	    $display ("    insert prediction [0x%0h] <= (%0h -> %0h)",
		      index_b, cf_info.from_PC, pred_PC);
      end
   endmethod

endmodule

// ================================================================

endpackage


use DmaOffsetSize everywhere, pushing the type problem to a higher level

// This is a fifo implmented with a ring buffer in main memory.
//
// SW->HW version
//  enqueue data in memory
//  update write pointer with portal request
// The hardware side activates when the write pointer is different than
// the read pointer, and copies data into an output BRAM fifo
//
// Software is responsible for knowing where the read pointer is,
// either by explicitly reading it with a portal-request -> portal indication
// or by other means
//
// Configuration.
import Dma::*;

interface RingBuffer;
   method Bool notEmpty();
   method Bool notFull();
   method Action push(Bit#(8) num);
   method Action pop(Bit#(8) num);
   interface Reg#(Bit#(DmaOffsetSize)) bufferfirst;
   interface Reg#(Bit#(DmaOffsetSize)) bufferlast;
   interface Reg#(Bool) enable;
   interface RingBufferConfig configifc;
   interface Reg#(DmaPointer) memhandle;
endinterface

interface RingBufferConfig;
   method Action set(Bit#(3) regist, Bit#(32) addr);
   method Bit#(32) get(Bit#(3) regist);
endinterface

module mkRingBuffer(RingBuffer);

   Reg#(Bit#(DmaOffsetSize)) rbufferbase <- mkReg(0);
   Reg#(Bit#(DmaOffsetSize)) rbufferend <- mkReg(0);
   Reg#(Bit#(DmaOffsetSize)) rbufferfirst <- mkReg(0);
   Reg#(Bit#(DmaOffsetSize)) rbufferlast <- mkReg(0);
   Reg#(Bit#(DmaOffsetSize)) rbuffermask <- mkReg(0);
   Reg#(DmaPointer) rmemhandle <- mkReg(0);
   Reg#(Bool) renable <- mkReg(False);

   interface RingBufferConfig configifc;
   method Action set(Bit#(3) regist, Bit#(DmaOffsetSize) addr);
      if (regist == 0) rbufferbase <= truncate(addr);
      else if (regist == 1) rbufferend <= truncate(addr);
      else if (regist == 2) rbufferfirst <= truncate(addr);
      else if (regist == 3) rbufferlast <= truncate(addr);
      else if (regist == 4) rbuffermask <= truncate(addr);
      else if (regist == 5) rmemhandle <= truncate(addr);
      else renable <= (addr[0] != 0);
   endmethod

   method Bit#(DmaOffsetSize) get(Bit#(3) regist);
      if (regist == 0) return (zeroExtend(rbufferbase));
      else if (regist == 1) return (zeroExtend(rbufferend));
      else if (regist == 2) return (zeroExtend(rbufferfirst));
      else if (regist == 3) return (zeroExtend(rbufferlast));
      else if (regist == 4) return (zeroExtend(rbuffermask));
      else if (regist == 5) return (zeroExtend(rmemhandle));
      else return(zeroExtend(pack(renable)));
   endmethod
   endinterface

   method Bool notEmpty();
   return (rbufferfirst != rbufferlast);
   endmethod

   method Bool notFull();
   return (((rbufferfirst + 64) & rbuffermask) != rbufferlast);
   endmethod

   method Action push(Bit#(8) num);
   rbufferfirst <= (rbufferfirst + zeroExtend(num)) & rbuffermask;
   endmethod

   method Action pop(Bit#(8) num);
   rbufferlast <= (rbufferlast + zeroExtend(num)) & rbuffermask;
   endmethod

   interface Reg bufferfirst = rbufferfirst;
   interface Reg bufferlast = rbufferlast;
   interface Reg enable = renable;
   interface Reg memhandle = rmemhandle;

endmodule

// This is a fifo implmented with a ring buffer in main memory.
//
// SW->HW version
//  enqueue data in memory
//  update write pointer with portal request
// The hardware side activates when the write pointer is different than
// the read pointer, and copies data into an output BRAM fifo
//
// Software is responsible for knowing where the read pointer is,
// either by explicitly reading it with a portal-request -> portal indication
// or by other means
//
// Configuration.
import Dma::*;

interface RingBuffer;
   method Bool notEmpty();
   method Bool notFull();
   method Action push(Bit#(8) num);
   method Action pop(Bit#(8) num);
   interface Reg#(Bit#(DmaOffsetSize)) bufferfirst;
   interface Reg#(Bit#(DmaOffsetSize)) bufferlast;
   interface Reg#(Bool) enable;
   interface RingBufferConfig configifc;
   interface Reg#(DmaPointer) memhandle;
endinterface

interface RingBufferConfig;
   method Action set(Bit#(3) regist, Bit#(32) addr);
   method Bit#(32) get(Bit#(3) regist);
endinterface

module mkRingBuffer(RingBuffer);

   Reg#(Bit#(DmaOffsetSize)) rbufferbase <- mkReg(0);
   Reg#(Bit#(DmaOffsetSize)) rbufferend <- mkReg(0);
   Reg#(Bit#(DmaOffsetSize)) rbufferfirst <- mkReg(0);
   Reg#(Bit#(DmaOffsetSize)) rbufferlast <- mkReg(0);
   Reg#(Bit#(DmaOffsetSize)) rbuffermask <- mkReg(0);
   Reg#(DmaPointer) rmemhandle <- mkReg(0);
   Reg#(Bool) renable <- mkReg(False);

   interface RingBufferConfig configifc;
   method Action set(Bit#(3) regist, Bit#(32) addr);
      if (regist == 0) rbufferbase <= truncate(addr);
      else if (regist == 1) rbufferend <= truncate(addr);
      else if (regist == 2) rbufferfirst <= truncate(addr);
      else if (regist == 3) rbufferlast <= truncate(addr);
      else if (regist == 4) rbuffermask <= truncate(addr);
      else if (regist == 5) rmemhandle <= truncate(addr);
      else renable <= (addr[0] != 0);
   endmethod

   method Bit#(32) get(Bit#(3) regist);
      if (regist == 0) return (zeroExtend(rbufferbase));
      else if (regist == 1) return (zeroExtend(rbufferend));
      else if (regist == 2) return (zeroExtend(rbufferfirst));
      else if (regist == 3) return (zeroExtend(rbufferlast));
      else if (regist == 4) return (zeroExtend(rbuffermask));
      else if (regist == 5) return (zeroExtend(rmemhandle));
      else return(zeroExtend(pack(renable)));
   endmethod
   endinterface

   method Bool notEmpty();
   return (rbufferfirst != rbufferlast);
   endmethod

   method Bool notFull();
   return (((rbufferfirst + 64) & rbuffermask) != rbufferlast);
   endmethod

   method Action push(Bit#(8) num);
   rbufferfirst <= (rbufferfirst + zeroExtend(num)) & rbuffermask;
   endmethod

   method Action pop(Bit#(8) num);
   rbufferlast <= (rbufferlast + zeroExtend(num)) & rbuffermask;
   endmethod

   interface Reg bufferfirst = rbufferfirst;
   interface Reg bufferlast = rbufferlast;
   interface Reg enable = renable;
   interface Reg memhandle = rmemhandle;

endmodule


add enable, change names

add enable, change names

// This is a fifo implmented with a ring buffer in main memory.
//
// SW->HW version
//  enqueue data in memory
//  update write pointer with portal request
// The hardware side activates when the write pointer is different than
// the read pointer, and copies data into an output BRAM fifo
//
// Software is responsible for knowing where the read pointer is,
// either by explicitly reading it with a portal-request -> portal indication
// or by other means
//
// Configuration.

interface RingBuffer;
   method Bool notEmpty();
   method Bool notFull();
   method Action push(UInt#(8) num);
   method Action pop(UInt#(8) num);
   interface Reg#(Bit#(40)) bufferfirst;
   interface Reg#(Bit#(40)) bufferlast;
   interface Reg#(Bool) enable;
   interface RingBufferConfig configifc;
endinterface

interface RingBufferConfig;
   method Action set(Bit#(2) regist, Bit#(40) addr);
   method Bit#(40) get(Bit#(2) regist);
endinterface

module mkRingBuffer(RingBuffer);

   Reg#(Bit#(40)) bufferbase <- mkReg(0);
   Reg#(Bit#(40)) bufferend <- mkReg(0);
   Reg#(Bit#(40)) bufferfirst <- mkReg(0);
   Reg#(Bit#(40)) bufferlast <- mkReg(0);
   Reg#(Bit#(40)) buffermask <- mkReg(0);
   Reg#(Bool) hwenable <- mkReg(False);

   interface RingBufferConfig configifc;
   method Action set(Bit#(2) regist, Bit#(40) addr);
      if (regist == 0) bufferbase <= addr;
      else if (regist == 1) bufferend <= addr;
      else if (regist == 2) bufferfirst <= addr;
      else if (regist == 3) bufferlast <= addr;
      else if (regist == 4) buffermask <= addr;
      else hwenable <= (addr[0] != 0);
   endmethod

   method Bit#(40) get(Bit#(2) regist);
      if (regist == 0) return (bufferbase);
      else if (regist == 1) return (bufferend);
      else if (regist == 2) return (bufferfirst);
      else if (regist == 3) return (bufferlast);
      else if (regist == 4) return (buffermask);
      else return(hwenable);
   endmethod
   endinterface

   method Bool notEmpty();
   return (bufferfirst != bufferlast);
   endmethod

   method Bool notFull();
   return (((bufferfirst + 64) & buffermask) != bufferlast);
   endmethod

   method Action push(UInt#(8) num);
   bufferfirst <= (bufferfirst + num) & buffermask;
   endmethod

   method Action pop(UInt#(8) num);
   bufferlast <= (bufferlast + num) & buffermask;
   endmethod

   interface Reg bufferFirst = bufferFirst;
   interface Reg bufferLast = bufferlast;
   interface Reg enable = hwenable;

endmodule

// This is a fifo implmented with a ring buffer in main memory.
//
// SW->HW version
//  enqueue data in memory
//  update write pointer with portal request
// The hardware side activates when the write pointer is different than
// the read pointer, and copies data into an output BRAM fifo
//
// Software is responsible for knowing where the read pointer is,
// either by explicitly reading it with a portal-request -> portal indication
// or by other means
//
// Configuration.
interface RingBuffer;
//   method Get#(CommandStruct);
//   method Put#(StatusStruct);
   method Bool notEmpty();
   method Bool notFull();
   method Action push(UInt num);
   method Action pop(UInt num);
   interface Reg#(Bit#(40)) expBufferFirst;
   interface Reg#(Bit#(40)) expBufferLast;
   interface RingBufferConfig configifc;
endinterface

interface RingBufferConfig;
   method Action set(Bit#(2) regist, Bit#(40) addr);
   method Bit#(40) get(Bit#(2) regist);
endinterface

module mkRingBuffer(RingBuffer);

   Reg#(Bit#(40)) bufferbase <- mkReg(0);
   Reg#(Bit#(40)) bufferend <- mkReg(0);
   Reg#(Bit#(40)) bufferfirst <- mkReg(0);
   Reg#(Bit#(40)) bufferlast <- mkReg(0);
   Reg#(Bit#(40)) buffermask <- mkReg(0);

   interface RingBufferConfig configifc;
   method Action set(Bit#(2) regist, Bit#(40) addr);
      if (regist == 0) bufferbase <= addr;
      else if (regist == 1) bufferend <= addr;
      else if (regist == 2) bufferfirst <= addr;
      else if (regist == 3) bufferlast <= addr;
      else buffermask <= addr;
   endmethod

   method Bit#(40) get(Bit#(2) regist);
      if (regist == 0) return (bufferbase);
      else if (regist == 1) return (bufferend);
      else if (regist == 2) return (bufferfirst);
      else return(bufferlast);
   endmethod
   endinterface

   method Bool notEmpty();
   return (bufferfirst != bufferlast);
   endmethod

   method Bool notFull();
   return (((bufferfirst + 64) & buffermask) != bufferlast);
   endmethod

   method Action push(UInt num);
   bufferfirst <= (bufferfirst + num) & buffermask;
   endmethod

   method Action pop(UInt num);
   bufferlast <= (bufferlast + num) & buffermask;
   endmethod

   interface Reg expBufferLast = bufferlast;

endmodule