fix amsbits

fix amsbits

Change-Id: I676ea54df15d1da4dca5a84d517eecc895aabcd9

import FIFO::*;
import SpecialFIFOs::*;
import Complex::*;
import FIFOF::*;
import Vector::*;
import StmtFSM::*;

interface PipeMul#(numeric type stages, numeric type dsz, type marker);
   method Action put(UInt#(dsz) x, UInt#(dsz) y, marker m);
   method ActionValue#(Tuple2#(UInt#(dsz),marker)) get();
endinterface

module mkPipeMul(PipeMul#(stages,dsz,marker))
   provisos(Mul#(2,dsz,buff_width),
	    Add#(a__,dsz,buff_width),
	    Bits#(marker, b__));

   Vector#(stages, Reg#(UInt#(buff_width))) mul_data <- replicateM(mkReg(0));
   Vector#(TAdd#(stages,1),FIFO#(marker)) mul_ctrl <- replicateM(mkLFIFO);

   Reg#(UInt#(dsz))  a <- mkRegU;
   Reg#(UInt#(dsz))  b <- mkRegU;
   FIFO#(UInt#(dsz)) out <- mkLFIFO;
   FIFO#(Tuple3#(UInt#(dsz),UInt#(dsz),marker)) inf <- mkLFIFO;
   FIFO#(Tuple2#(UInt#(dsz),marker)) outf <- mkLFIFO;

   rule do_mul;
      UInt#(buff_width) bits = extend(b) * extend(a);
      mul_data[0] <= bits;
      for(Integer i = 1; i < valueOf(stages); i = i+1)
      	 mul_data[i] <= mul_data[i-1];
   endrule

   for(Integer i = 0; i < valueOf(stages); i = i+1)
      rule do_ctrl;
   	 mul_ctrl[i+1].enq(mul_ctrl[i].first);
   	 mul_ctrl[i].deq;
      endrule

   rule final_xfer;
      mul_ctrl[valueOf(stages)].deq;
      UInt#(buff_width) bits = mul_data[valueOf(stages)-1];
      UInt#(dsz) rv  = truncate(bits);
      outf.enq(tuple2(rv,mul_ctrl[valueOf(stages)].first));
   endrule

   rule start;
      inf.deq;
      a <= tpl_1(inf.first);
      b <= tpl_2(inf.first);
      mul_ctrl[0].enq(tpl_3(inf.first));
   endrule

   method Action put(UInt#(dsz) x, UInt#(dsz) y, marker m);
      inf.enq(tuple3(x,y,m));
   endmethod

   method ActionValue#(Tuple2#(UInt#(dsz),marker)) get();
      outf.deq;
      return outf.first;
   endmethod

endmodule

interface PipeMul2#(numeric type stages, numeric type dsz, type marker);
   method Action put(UInt#(dsz) x, UInt#(dsz) y, marker m);
   method ActionValue#(Tuple2#(UInt#(TMul#(dsz,2)),marker)) get();
endinterface

module mkPipeMul2(PipeMul2#(2,dsz,marker))
   provisos(Add#(2,0,stages),
	    Mul#(2,dsz,odsz),
	    Add#(16,aszm16,dsz),
	    Add#(16,c__,odsz),
	    Add#(a__,dsz,odsz),
	    Bits#(marker, b__));

   Vector#(stages, Reg#(Tuple2#(UInt#(odsz),UInt#(odsz)))) mul_data <- replicateM(mkReg(unpack(0)));
   Vector#(TAdd#(stages,1),FIFO#(marker)) mul_ctrl <- replicateM(mkLFIFO);

   Reg#(UInt#(dsz))  a <- mkRegU;
   Reg#(UInt#(dsz))  b <- mkRegU;
   FIFO#(UInt#(dsz)) out <- mkLFIFO;
   FIFO#(Tuple3#(UInt#(dsz),UInt#(dsz),marker)) inf <- mkLFIFO;
   FIFO#(Tuple2#(UInt#(odsz),marker)) outf <- mkLFIFO;

   rule do_mul;
      UInt#(odsz) lsbits = extend(b) * extend(unpack(pack(a)[15:0]));
      UInt#(odsz) amsbits = unpack(pack(a)[valueOf(dsz)-1:16]);
      UInt#(odsz) msbits = extend(b) * amsbits;
      mul_data[0] <= tuple2(lsbits,msbits);
   endrule
   rule do_add;
      match { .lsbits, .msbits } = mul_data[0];
      UInt#(odsz) prod = lsbits + (msbits << 16);
      mul_data[1] <= tuple2(0, prod);
   endrule

   for(Integer i = 0; i < valueOf(stages); i = i+1)
      rule do_ctrl;
   	 mul_ctrl[i+1].enq(mul_ctrl[i].first);
   	 mul_ctrl[i].deq;
      endrule

   rule final_xfer;
      mul_ctrl[valueOf(stages)].deq;
      match { .zero, .rv } = mul_data[valueOf(stages)-1];
      outf.enq(tuple2(rv,mul_ctrl[valueOf(stages)].first));
   endrule

   rule start;
      inf.deq;
      a <= tpl_1(inf.first);
      b <= tpl_2(inf.first);
      mul_ctrl[0].enq(tpl_3(inf.first));
   endrule

   method Action put(UInt#(dsz) x, UInt#(dsz) y, marker m);
      inf.enq(tuple3(x,y,m));
   endmethod

   method ActionValue#(Tuple2#(UInt#(odsz),marker)) get();
      outf.deq;
      return outf.first;
   endmethod

endmodule

// module mkTestBench();
//    PipeMul#(1,FixedPoint#(8,24)) multiplier <- mkPipeMul;
//    Stmt test =
//    seq
//       multiplier.put(fromReal(-1.0),fromReal(2.0));
//       action
// 	 let rv <- multiplier.get;
// 	 $write("mul: ");
// 	 dispFP824(rv);
//       endaction
//    endseq;
//    mkAutoFSM(test);
// endmodule

import FIFO::*;
import SpecialFIFOs::*;
import Complex::*;
import FIFOF::*;
import Vector::*;
import StmtFSM::*;

interface PipeMul#(numeric type stages, numeric type dsz, type marker);
   method Action put(UInt#(dsz) x, UInt#(dsz) y, marker m);
   method ActionValue#(Tuple2#(UInt#(dsz),marker)) get();
endinterface

module mkPipeMul(PipeMul#(stages,dsz,marker))
   provisos(Mul#(2,dsz,buff_width),
	    Add#(a__,dsz,buff_width),
	    Bits#(marker, b__));

   Vector#(stages, Reg#(UInt#(buff_width))) mul_data <- replicateM(mkReg(0));
   Vector#(TAdd#(stages,1),FIFO#(marker)) mul_ctrl <- replicateM(mkLFIFO);

   Reg#(UInt#(dsz))  a <- mkRegU;
   Reg#(UInt#(dsz))  b <- mkRegU;
   FIFO#(UInt#(dsz)) out <- mkLFIFO;
   FIFO#(Tuple3#(UInt#(dsz),UInt#(dsz),marker)) inf <- mkLFIFO;
   FIFO#(Tuple2#(UInt#(dsz),marker)) outf <- mkLFIFO;

   rule do_mul;
      UInt#(buff_width) bits = extend(b) * extend(a);
      mul_data[0] <= bits;
      for(Integer i = 1; i < valueOf(stages); i = i+1)
      	 mul_data[i] <= mul_data[i-1];
   endrule

   for(Integer i = 0; i < valueOf(stages); i = i+1)
      rule do_ctrl;
   	 mul_ctrl[i+1].enq(mul_ctrl[i].first);
   	 mul_ctrl[i].deq;
      endrule

   rule final_xfer;
      mul_ctrl[valueOf(stages)].deq;
      UInt#(buff_width) bits = mul_data[valueOf(stages)-1];
      UInt#(dsz) rv  = truncate(bits);
      outf.enq(tuple2(rv,mul_ctrl[valueOf(stages)].first));
   endrule

   rule start;
      inf.deq;
      a <= tpl_1(inf.first);
      b <= tpl_2(inf.first);
      mul_ctrl[0].enq(tpl_3(inf.first));
   endrule

   method Action put(UInt#(dsz) x, UInt#(dsz) y, marker m);
      inf.enq(tuple3(x,y,m));
   endmethod

   method ActionValue#(Tuple2#(UInt#(dsz),marker)) get();
      outf.deq;
      return outf.first;
   endmethod

endmodule

interface PipeMul2#(numeric type stages, numeric type dsz, type marker);
   method Action put(UInt#(dsz) x, UInt#(dsz) y, marker m);
   method ActionValue#(Tuple2#(UInt#(TMul#(dsz,2)),marker)) get();
endinterface

module mkPipeMul2(PipeMul2#(2,dsz,marker))
   provisos(Add#(2,0,stages),
	    Mul#(2,dsz,odsz),
	    Add#(16,aszm16,dsz),
	    Add#(16,c__,odsz),
	    Add#(a__,dsz,odsz),
	    Bits#(marker, b__));

   Vector#(stages, Reg#(Tuple2#(UInt#(odsz),UInt#(odsz)))) mul_data <- replicateM(mkReg(unpack(0)));
   Vector#(TAdd#(stages,1),FIFO#(marker)) mul_ctrl <- replicateM(mkLFIFO);

   Reg#(UInt#(dsz))  a <- mkRegU;
   Reg#(UInt#(dsz))  b <- mkRegU;
   FIFO#(UInt#(dsz)) out <- mkLFIFO;
   FIFO#(Tuple3#(UInt#(dsz),UInt#(dsz),marker)) inf <- mkLFIFO;
   FIFO#(Tuple2#(UInt#(odsz),marker)) outf <- mkLFIFO;

   rule do_mul;
      UInt#(odsz) lsbits = extend(b) * extend(unpack(pack(a)[15:0]));
      UInt#(odsz) amsbits = unpack(pack(a)[valueOf(dsz)-1:valueOf(aszm16)]);
      UInt#(odsz) msbits = extend(b) * amsbits;
      mul_data[0] <= tuple2(lsbits,msbits);
   endrule
   rule do_add;
      match { .lsbits, .msbits } = mul_data[0];
      UInt#(odsz) prod = lsbits + (msbits << 16);
      mul_data[1] <= tuple2(0, prod);
   endrule

   for(Integer i = 0; i < valueOf(stages); i = i+1)
      rule do_ctrl;
   	 mul_ctrl[i+1].enq(mul_ctrl[i].first);
   	 mul_ctrl[i].deq;
      endrule

   rule final_xfer;
      mul_ctrl[valueOf(stages)].deq;
      match { .zero, .rv } = mul_data[valueOf(stages)-1];
      outf.enq(tuple2(rv,mul_ctrl[valueOf(stages)].first));
   endrule

   rule start;
      inf.deq;
      a <= tpl_1(inf.first);
      b <= tpl_2(inf.first);
      mul_ctrl[0].enq(tpl_3(inf.first));
   endrule

   method Action put(UInt#(dsz) x, UInt#(dsz) y, marker m);
      inf.enq(tuple3(x,y,m));
   endmethod

   method ActionValue#(Tuple2#(UInt#(odsz),marker)) get();
      outf.deq;
      return outf.first;
   endmethod

endmodule

// module mkTestBench();
//    PipeMul#(1,FixedPoint#(8,24)) multiplier <- mkPipeMul;
//    Stmt test =
//    seq
//       multiplier.put(fromReal(-1.0),fromReal(2.0));
//       action
// 	 let rv <- multiplier.get;
// 	 $write("mul: ");
// 	 dispFP824(rv);
//       endaction
//    endseq;
//    mkAutoFSM(test);
// endmodule


fix names of registers

fix names of registers

// Copyright (c) 2014 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 StmtFSM::*;
import BRAM::*;
import MCSAlgorithm::*;

module mkHirschA#(BRAMServer#(Bit#(strIndexWidth), Bit#(8)) strA, BRAMServer#(Bit#(strIndexWidth), Bit#(8)) strB, BRAMServer#(Bit#(lIndexWidth), Bit#(16)) matL)(MCSAlgorithm)
      provisos(Add#(0, 7, strIndexWidth),
	       Add#(0, 14, lIndexWidth));

    Reg#(Bit#(7)) aLenReg <- mkReg(21);
  Reg#(Bit#(7)) bLenReg <- mkReg(18);
  Reg#(Bit#(14)) rLenReg <- mkReg(0);
  Reg#(Bit#(7)) ii <- mkReg(0);
  Reg#(Bit#(7)) jj <- mkReg(0);
   Reg#(Bit#(8)) aData <- mkReg(0);
   Reg#(Bit#(8)) bData <- mkReg(0);
   Reg#(Bit#(16)) lim1jm1 <- mkReg(0);
   Reg#(Bit#(16)) lim1j <- mkReg(0);
   Reg#(Bit#(16)) lijm1 <- mkReg(0);

   Stmt hirschA =
   seq
//      $display("hirschA running alen %d blen %d", aLenReg, bLenReg);
      for (ii<= 0; ii < aLenReg; ii <= ii + 1)
	 seq
	    matL.request.put(BRAMRequest{write: True, responseOnWrite: False, address: {ii,0}, datain: 0});
	    endseq
      for (ii<= 0; ii < aLenReg; ii <= ii + 1)
	 seq
	    matL.request.put(BRAMRequest{write: True, responseOnWrite: False, address: {0,ii}, datain: 0});
	 endseq
      for (ii<= 1; ii <= aLenReg; ii <= ii + 1)
	 for (jj<= 1; jj <= bLenReg; jj <= jj + 1)
	    seq
	       strA.request.put(BRAMRequest{write: False, responseOnWrite: False, address: ii-1, datain: 0});
	       strB.request.put(BRAMRequest{write: False, responseOnWrite: False, address: jj-1, datain: 0});
	       action
		  let ta <- strA.response.get();
		  let tb <- strB.response.get();
		  aData <= ta;
		  bData <= tb;
	       endaction
	       if (aData == bData)
		  seq
		     matL.request.put(BRAMRequest{write: False, responseOnWrite: False, address: {ii-1,jj-1}, datain: 0});
		     action
			let temp <- matL.response.get();
			lim1jm1 <= temp;
		     endaction
		     matL.request.put(BRAMRequest{write: True, responseOnWrite: False, address: {ii,jj}, datain: lim1jm1+1});

		  endseq
	       else
		  seq
		     matL.request.put(BRAMRequest{write: False, responseOnWrite: False, address: {ii,jj-1}, datain: 0});
		     action
			let tlijm1 <- matL.response.get();
			lijm1 <= tlijm1;
		     endaction
		     matL.request.put(BRAMRequest{write: False, responseOnWrite: False, address: {ii-1,jj}, datain: 0});
		     action
			let tlim1j <- matL.response.get();
			lim1j <= tlim1j;
		     endaction
			matL.request.put(BRAMRequest{write: True, responseOnWrite: False, address: {ii,jj}, datain: max(lijm1,lim1j)});
		  endseq
	    endseq
   endseq;

   FSM hA <- mkFSM(hirschA);

   method Action setupA(Bit#(7) strLen);
      aLenReg <= strLen;
   endmethod

   method Action setupB(Bit#(7) strLen);
      bLenReg <= strLen;
   endmethod

   interface FSM fsm = hA;

endmodule

// Copyright (c) 2014 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 StmtFSM::*;
import BRAM::*;
import MCSAlgorithm::*;

module mkHirschA#(BRAMServer#(Bit#(strIndexWidth), Bit#(8)) strA, BRAMServer#(Bit#(strIndexWidth), Bit#(8)) strB, BRAMServer#(Bit#(lIndexWidth), Bit#(16)) matL)(MCSAlgorithm)
      provisos(Add#(0, 7, strIndexWidth),
	       Add#(0, 14, lIndexWidth));

    Reg#(Bit#(7)) aLenReg <- mkReg(21);
  Reg#(Bit#(7)) bLenReg <- mkReg(18);
  Reg#(Bit#(14)) rLenReg <- mkReg(0);
  Reg#(Bit#(7)) ii <- mkReg(0);
  Reg#(Bit#(7)) jj <- mkReg(0);
   Reg#(Bit#(8)) aData <- mkReg(0);
   Reg#(Bit#(8)) bData <- mkReg(0);
   Reg#(Bit#(16)) lim1jm1 <- mkReg(0);
   Reg#(Bit#(16)) lim1j <- mkReg(0);
   Reg#(Bit#(16)) lijm1 <- mkReg(0);

   Stmt hirschA =
   seq
//      $display("hirschA running alen %d blen %d", aLenReg, bLenReg);
      for (ii<= 0; ii < aLenReg; ii <= ii + 1)
	 seq
	    matL.request.put(BRAMRequest{write: True, responseOnWrite: False, address: {ii,0}, datain: 0});
	    endseq
      for (ii<= 0; ii < aLenReg; ii <= ii + 1)
	 seq
	    matL.request.put(BRAMRequest{write: True, responseOnWrite: False, address: {0,ii}, datain: 0});
	 endseq
      for (ii<= 1; ii <= aLenReg; ii <= ii + 1)
	 for (jj<= 1; jj <= bLenReg; jj <= jj + 1)
	    seq
	       strA.request.put(BRAMRequest{write: False, responseOnWrite: False, address: ii-1, datain: 0});
	       strB.request.put(BRAMRequest{write: False, responseOnWrite: False, address: jj-1, datain: 0});
	       action
		  let ta <- strA.response.get();
		  let tb <- strB.response.get();
		  aData <= ta;
		  bData <= tb;
	       endaction
	       if (aData == bData)
		  seq
		     matL.request.put(BRAMRequest{write: False, responseOnWrite: False, address: {ii-1,jj-1}, datain: 0});
		     action
			let temp <- matL.response.get();
			lim1jm1 <= temp;
		     endaction
		     matL.request.put(BRAMRequest{write: True, responseOnWrite: False, address: {ii,jj}, datain: lim1jm1+1});

		  endseq
	       else
		  seq
		     matL.request.put(BRAMRequest{write: False, responseOnWrite: False, address: {ii,jj-1}, datain: 0});
		     action
			let tlijm1 <- matL.response.get();
			lijm1 <= tlijm1;
		     endaction
		     matL.request.put(BRAMRequest{write: False, responseOnWrite: False, address: {ii-1,jj}, datain: 0});
		     action
			let tlim1j <- matL.response.get();
			lim1j <= tlim1j;
		     endaction
			matL.request.put(BRAMRequest{write: True, responseOnWrite: False, address: {ii,jj}, datain: max(lijm1,lim1j)});
		  endseq
	    endseq
   endseq;

   FSM hA <- mkFSM(hirschA);

   method Action setupA(Bit#(7) strLen);
      strLenA <= strLen;
   endmethod

   method Action setupB(Bit#(7) strLen);
      strLenB <= strLen;
   endmethod

   interface FSM fsm = hA;

endmodule


fixed clock domains on registers and wires

// Copyright (c) 2015 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 Clocks::*;
import Vector::*;
import FIFO::*;
import FIFOF::*;
import BRAMFIFO::*;
import CBus::*; // extendNP and truncateNP

import Arith::*;

(* always_ready, always_enabled *)
interface X7FifoSyncMacro#(numeric type data_width);
   method Bit#(1) empty();
   method Bit#(1) full();
   method Action din(Bit#(data_width) v);
   method Action wren(Bit#(1) wren);
   method Bit#(data_width) dout();
   method Action rden(Bit#(1) rden);
   method Bit#(9) wrcount();
   method Bit#(9) rdcount();
endinterface

import "BVI" FIFO_DUALCLOCK_MACRO =
module  vmkBramFifo#(String fifo_size, Clock wrClock, Reset wrReset, Clock rdClock, Reset rdReset)(X7FifoSyncMacro#(data_width));
   let invertReset <- mkResetInverter(wrReset);
   parameter DEVICE = "7SERIES";
   parameter DATA_WIDTH = valueOf(data_width);
   parameter FIFO_SIZE = fifo_size;
   parameter FIRST_WORD_FALL_THROUGH = 1;
   default_clock wrClock(WRCLK) = wrClock;
   default_reset wrReset(RST) = invertReset;
   input_clock rdClock(RDCLK) = rdClock;
   method EMPTY empty() clocked_by (rdClock) reset_by (wrReset);
   method FULL full() clocked_by (wrClock) reset_by (wrReset);
   method din(DI) enable ((*inhigh*)EN_di) clocked_by (wrClock) reset_by (wrReset);
   method wren(WREN) enable ((*inhigh*)EN_wren) clocked_by (wrClock) reset_by (wrReset);
   method DO dout() clocked_by (rdClock) reset_by (wrReset);
   method rden(RDEN) enable ((*inhigh*)EN_rden) clocked_by (rdClock) reset_by (wrReset);
   // wrcount and rdcount ports are needed for xsim
   method WRCOUNT wrcount() clocked_by (wrClock) reset_by (wrReset);
   method RDCOUNT rdcount() clocked_by (rdClock) reset_by (wrReset);
   schedule (empty, full, dout, din, wren, rden, wrcount, rdcount) CF (empty, full, dout, din, wren, rden, wrcount, rdcount);
endmodule

module mkDualClockBramFIFOF#(Clock srcClock, Reset srcReset, Clock dstClock, Reset dstReset)(FIFOF#(t))
   provisos (Bits#(t,sizet),
	     Add#(1,a__,sizet));
   String fifo_size = "18Kb";
   Vector#(TDiv#(sizet,36),X7FifoSyncMacro#(36)) fifos <- replicateM(vmkBramFifo(fifo_size, srcClock, srcReset, dstClock, dstReset));
   Wire#(Bit#(1)) rdenWire <- mkDWire(0, clocked_by dstClock, reset_by dstReset);
   Wire#(Bit#(1)) wrenWire <- mkDWire(0, clocked_by srcClock, reset_by srcReset);
   Vector#(TDiv#(sizet,36),Wire#(Bit#(36))) dinWires <- replicateM(mkDWire(0, clocked_by srcClock, reset_by srcReset));

   for (Integer i = 0; i < valueOf(TDiv#(sizet,36)); i = i+1) begin
      Reg#(Bit#(9)) rdcount <- mkReg(0, clocked_by dstClock, reset_by dstReset);
      Reg#(Bit#(9)) wrcount <- mkReg(0, clocked_by srcClock, reset_by srcReset);
      rule rdenRule;
	 fifos[i].rden(rdenWire);
      endrule
      rule wrenRule;
	 fifos[i].wren(wrenWire);
      endrule
      rule inputs;
	 fifos[i].din(dinWires[i]);
      endrule
      rule countrds;
	 rdcount <= fifos[i].rdcount();
      endrule
      rule countwrs;
	 wrcount <= fifos[i].wrcount();
      endrule
   end

   function Bool fifoNotEmpty(Integer i); return fifos[i].empty == 0; endfunction
   function Bool fifoNotFull(Integer i); return fifos[i].full == 0; endfunction
   Vector#(TDiv#(sizet,36), Bool) vNotEmpty = genWith(fifoNotEmpty);
   Vector#(TDiv#(sizet,36), Bool) vNotFull = genWith(fifoNotFull);

   method t first() if (fifos[0].empty == 0);
      function Bit#(36) fifoFirst(Integer i); return fifos[i].dout(); endfunction
      Vector#(TDiv#(sizet,36), Bit#(36)) v = genWith(fifoFirst);
      return unpack(truncateNP(pack(v)));
   endmethod
   method Action deq() if (fifos[0].empty == 0);
      rdenWire <= 1;
   endmethod
   method notEmpty = (fifos[0].empty == 0);
   method Action enq(t v) if (fifos[0].full == 0);
      Vector#(TDiv#(sizet,36), Bit#(36)) vs = unpack(extendNP(pack(v)));
      Vector#(TDiv#(sizet,36), Integer) indices = genVector();
      function Action fifoEnq(Integer i); action dinWires[i] <= vs[i]; endaction endfunction
      mapM_(fifoEnq, indices);
      wrenWire <= 1;
   endmethod
   method notFull = (fifos[0].full == 0);
endmodule

module mkSizedBRAMFIFOF#(Integer m)(FIFOF#(t))
   provisos (Bits#(t,sizet),
	     Add#(1,a__,sizet));
   let clock <- exposeCurrentClock();
   let reset <- exposeCurrentReset();
   let fifo <- mkDualClockBramFIFOF(clock, reset, clock, reset);
   return fifo;
endmodule
module mkSizedBRAMFIFO#(Integer m)(FIFO#(t))
   provisos (Bits#(t,sizet),
	     Add#(1,a__,sizet));
   let clock <- exposeCurrentClock();
   let reset <- exposeCurrentReset();
   let fifo <- mkDualClockBramFIFOF(clock, reset, clock, reset);
   method first = fifo.first;
   method deq = fifo.deq;
   method enq = fifo.enq;
endmodule

// Copyright (c) 2015 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 Clocks::*;
import Vector::*;
import FIFO::*;
import FIFOF::*;
import BRAMFIFO::*;
import CBus::*; // extendNP and truncateNP

import Arith::*;

(* always_ready, always_enabled *)
interface X7FifoSyncMacro#(numeric type data_width);
   method Bit#(1) empty();
   method Bit#(1) full();
   method Action din(Bit#(data_width) v);
   method Action wren(Bit#(1) wren);
   method Bit#(data_width) dout();
   method Action rden(Bit#(1) rden);
   method Bit#(9) wrcount();
   method Bit#(9) rdcount();
endinterface

import "BVI" FIFO_DUALCLOCK_MACRO =
module  vmkBramFifo#(String fifo_size, Clock wrClock, Reset wrReset, Clock rdClock, Reset rdReset)(X7FifoSyncMacro#(data_width));
   let invertReset <- mkResetInverter(wrReset);
   parameter DEVICE = "7SERIES";
   parameter DATA_WIDTH = valueOf(data_width);
   parameter FIFO_SIZE = fifo_size;
   parameter FIRST_WORD_FALL_THROUGH = 1;
   default_clock wrClock(WRCLK) = wrClock;
   default_reset wrReset(RST) = invertReset;
   input_clock rdClock(RDCLK) = rdClock;
   method EMPTY empty() clocked_by (rdClock) reset_by (wrReset);
   method FULL full() clocked_by (wrClock) reset_by (wrReset);
   method din(DI) enable ((*inhigh*)EN_di) clocked_by (wrClock) reset_by (wrReset);
   method wren(WREN) enable ((*inhigh*)EN_wren) clocked_by (wrClock) reset_by (wrReset);
   method DO dout() clocked_by (rdClock) reset_by (wrReset);
   method rden(RDEN) enable ((*inhigh*)EN_rden) clocked_by (rdClock) reset_by (wrReset);
   // wrcount and rdcount ports are needed for xsim
   method WRCOUNT wrcount() clocked_by (wrClock) reset_by (wrReset);
   method RDCOUNT rdcount() clocked_by (rdClock) reset_by (wrReset);
   schedule (empty, full, dout, din, wren, rden, wrcount, rdcount) CF (empty, full, dout, din, wren, rden, wrcount, rdcount);
endmodule

module mkDualClockBramFIFOF#(Clock srcClock, Reset srcReset, Clock dstClock, Reset dstReset)(FIFOF#(t))
   provisos (Bits#(t,sizet),
	     Add#(1,a__,sizet));
   String fifo_size = "36Kb";
   Vector#(TDiv#(sizet,72),X7FifoSyncMacro#(72)) fifos <- replicateM(vmkBramFifo(fifo_size, srcClock, srcReset, dstClock, dstReset));
   Wire#(Bit#(1)) rdenWire <- mkDWire(0);
   Wire#(Bit#(1)) wrenWire <- mkDWire(0);
   Vector#(TDiv#(sizet,72),Wire#(Bit#(72))) dinWires <- replicateM(mkDWire(0));

   for (Integer i = 0; i < valueOf(TDiv#(sizet,72)); i = i+1) begin
      Reg#(Bit#(9)) rdcount <- mkReg(0);
      Reg#(Bit#(9)) wrcount <- mkReg(0);
      rule enables;
	 fifos[i].rden(rdenWire);
	 fifos[i].wren(wrenWire);
      endrule
      rule inputs;
	 fifos[i].din(dinWires[i]);
      endrule
      rule counts;
	 rdcount <= fifos[i].rdcount();
	 wrcount <= fifos[i].wrcount();
      endrule
   end

   function Bool fifoNotEmpty(Integer i); return fifos[i].empty == 0; endfunction
   function Bool fifoNotFull(Integer i); return fifos[i].full == 0; endfunction
   Vector#(TDiv#(sizet,72), Bool) vNotEmpty = genWith(fifoNotEmpty);
   Vector#(TDiv#(sizet,72), Bool) vNotFull = genWith(fifoNotFull);

   method t first() if (fifos[0].empty == 0);
      function Bit#(72) fifoFirst(Integer i); return fifos[i].dout(); endfunction
      Vector#(TDiv#(sizet,72), Bit#(72)) v = genWith(fifoFirst);
      return unpack(truncateNP(pack(v)));
   endmethod
   method Action deq() if (fifos[0].empty == 0);
      rdenWire <= 1;
   endmethod
   method notEmpty = (fifos[0].empty == 0);
   method Action enq(t v) if (fifos[0].full == 0);
      Vector#(TDiv#(sizet,72), Bit#(72)) vs = unpack(extendNP(pack(v)));
      Vector#(TDiv#(sizet,72), Integer) indices = genVector();
      function Action fifoEnq(Integer i); action dinWires[i] <= vs[i]; endaction endfunction
      mapM_(fifoEnq, indices);
      wrenWire <= 1;
   endmethod
   method notFull = (fifos[0].full == 0);
endmodule

module mkSizedBRAMFIFOF#(Integer m)(FIFOF#(t))
   provisos (Bits#(t,sizet),
	     Add#(1,a__,sizet));
   let clock <- exposeCurrentClock();
   let reset <- exposeCurrentReset();
   let fifo <- mkDualClockBramFIFOF(clock, reset, clock, reset);
   return fifo;
endmodule
module mkSizedBRAMFIFO#(Integer m)(FIFO#(t))
   provisos (Bits#(t,sizet),
	     Add#(1,a__,sizet));
   let clock <- exposeCurrentClock();
   let reset <- exposeCurrentReset();
   let fifo <- mkDualClockBramFIFOF(clock, reset, clock, reset);
   method first = fifo.first;
   method deq = fifo.deq;
   method enq = fifo.enq;
endmodule


[RISCV] simple memory interface; debug information on LUI/OP_IMM

// Copyright (c) 2017 Min Chen.  All Rights Reserved.
// Author: Min Chen

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

import RegFile   :: *;    // For RISC-V GPRs
import ConfigReg :: *;

// ================================================================
// BSV project imports

import ISA_Decls :: *;    // Instruction encodings

// ================================================================
// Memory interface for CPU
// ================================================================

import BRAMCore :: *;

// ----------------
// IMem responses: either and exception or an instruction

typedef union tagged {
   Word      IMem_Resp_Ok;
} IMem_Resp deriving(Bits, FShow);

// ----------------
// DMem request ops and sizes

typedef enum {
   MEM_OP_LOAD,
   MEM_OP_STORE
} Mem_Op deriving(Eq, Bits, FShow);

// ----------------
// DMem requests

typedef struct {
   Mem_Op         mem_op;
   Mem_Data_Size  mem_data_size;
   Addr           addr;
   Word           data;          // Only relevant if mem_op == MEM_OP_STORE
} DMem_Req deriving(Bits, FShow);

// ----------------
// DMem responses: either an exception or data
// (data value is only relevant for LOADs, irrelevant for STOREs)

typedef union tagged {
   Exc_Code  DMem_Resp_Exception;
   Word      DMem_Resp_Ok;
} DMem_Resp deriving(Bits, FShow);

// ----------------
// Memory interface reference design

module mkMemory(Memory_IFC);
   BRAM_PORT#(Bit#(14), Word)             imem <- mkBRAMCore1Load(valueOf(TExp#(14)), False, "mem.vmh", False);
   BRAM_DUAL_PORT_BE#(Bit#(15), Word, 4)  dmem <- mkBRAMCore2BELoad(valueOf(TExp#(15)), False, "mem.vmh.D", False);
   Reg#(Bit#(2))                          reg_shift <- mkRegU;
   Reg#(Mem_Data_Size)                    reg_size  <- mkRegU;

   method Action imem_req(Addr addr);
      let phyAddr = addr - imemSt;
      imem.put(False, truncate(phyAddr >> 2), ?);
      //$display("[IMEM] Addr = 0x%08X", addr);
   endmethod

   method ActionValue#(IMem_Resp) imem_resp;
      return tagged IMem_Resp_Ok imem.read();
   endmethod

   method Action dmem_req(DMem_Req req);
      let phyAddr = (req.addr - dmemSt);
      Word val = req.data;
      Bit#(Bits_per_Word_Byte_Index) shift = truncate(req.addr);
      Bit#(Bytes_per_Word) mask = 4'b1111;
      Word pad = ?;

      reg_shift <= shift;
      reg_size <= req.mem_data_size;

      case(req.mem_data_size)
         BITS8: begin
            case(shift)
               0: begin
                  mask = 4'b0001;
               end
               2'd1: begin
                  mask = 4'b0010;
                  val = {pad[15:0], val[7:0], ?};
               end
               2: begin
                  mask = 4'b0100;
                  val = {pad[7:0], val[7:0], ?};
               end
               3: begin
                  mask = 4'b1000;
                  val = {val[7:0], ?};
               end
            endcase
         end
         BITS16: begin
            if (shift[0] != 0) begin
               $display("Unaligned memory access on 16-bits bound");
               $finish;
            end
            if (shift[1] == 0) begin
               mask = 4'b0011;
            end
            else begin
               mask = 4'b1100;
               val = {val[15:0], ?};
            end
         end
      endcase

      dmem.a.put((req.mem_op == MEM_OP_STORE) ? mask : 0, truncate(phyAddr >> 2), val);
      //$display("[DMEM] Addr = 0x%08X", req.addr);
   endmethod

   method ActionValue#(DMem_Resp) dmem_resp;
      Word v = dmem.a.read();

      case(reg_shift)
         0: v = v;
         1: v = {?, v[31: 8]};
         2: v = {?, v[31:16]};
         3: v = {?, v[31:24]};
      endcase

      return tagged DMem_Resp_Ok v;
   endmethod
endmodule

// ----------------------------------------------------------------
// This interface is an argument to the 'mkRISCV_Spec' module,
// and is used insided the module to access memory.
// MMUs, caches etc. are outside this boundary.

interface Memory_IFC;
   method Action                  imem_req(Addr addr);
   method ActionValue#(IMem_Resp) imem_resp;

   method Action                  dmem_req(DMem_Req req);
   method ActionValue#(DMem_Resp) dmem_resp;
endinterface

// ================================================================
// This interfacce is offered by the 'mkRISCV_Spec' module to the environment.
// It is not part of the spec, per se, and just has scaffolding that allows
// the environment to control and CPU and probe its state.

interface RISCV_IFC;
   method Action start(Addr initial_pc);
   method Bool   halted;

   method ActionValue#(Word) cpuToHost;
endinterface

// ================================================================
// The RISC-V CPU Specification module, 'mkRISCV'

// ----------------
// The CPU is initially in the IDLE state.
// It starts running when it is put into the FETCH state.
// When running, it cycles through the following sequences of states:
//
//     FETCH -> EXEC ->                     FINISH    for most instructions
//     FETCH -> EXEC -> EXEC_LD_RESPONSE -> FINISH    for LD instructions
//     FETCH -> EXEC -> EXEC_ST_RESPONSE -> FINISH    for ST instructions
//

typedef enum {STATE_IDLE,
              STATE_FETCH,
              STATE_EXEC,
              STATE_EXEC_LD_RESPONSE,
              STATE_EXEC_ST_RESPONSE,
              STATE_FINISH,
              STATE_HALT
   } CPU_STATE
deriving(Eq, Bits, FShow);

// ----------------
// Default fall-through PC

function Addr fv_fall_through_pc(Addr pc);
   return pc + 4;
endfunction: fv_fall_through_pc

// ----------------

module _mkRISCV#(Bit#(3) cfg_verbose)(RISCV_IFC);

   // internal components
   Memory_IFC           memory   <- mkMemory;

   // Program counter
   Reg#(Word) pc <- mkRegU;

   // General Purpose Registers
   RegFile#(RegName, Word) regfile  <- mkRegFileFull;

   // CSRs
   Reg#(Bit #(64))   csr_cycle   <- mkConfigReg(0);
   Reg#(Bit #(64))   csr_instret <- mkConfigReg(0);

   // ----------------
   // These CSRs are technically not present in the user-mode ISA.

   Reg#(Word)  csr_mepc       <- mkRegU;
   Reg#(Word)  csr_mcause     <- mkRegU;
   Reg#(Word)  csr_mbadaddr   <- mkRegU;

   Reg#(Maybe#(Word)) csr_dcsr <- mkReg(tagged Invalid);

   // ----------------------------------------------------------------
   // Non-architectural state, for this model

   Reg#(CPU_STATE)   cpu_state   <- mkReg(STATE_IDLE);
   Reg#(Addr)        rg_mem_addr <- mkRegU;    // Effective addr in LD/ST
   Reg#(Word)        rg_instr    <- mkRegU;    // Current instruction

   // ----------------------------------------------------------------
   // Read a CSR
   // If the addr is valid, return tagged Valid value
   // else return tagged Invalid

   function Maybe#(Word) fv_read_csr(CSR_Addr csr_addr);
      if      (csr_addr == csr_CYCLE)     return tagged Valid truncate   (csr_cycle);
      else if (csr_addr == csr_INSTRET)   return tagged Valid truncate   (csr_instret);

      else if (csr_addr == csr_CYCLEH  )  return tagged Valid truncateLSB(csr_cycle);
      else if (csr_addr == csr_INSTRETH)  return tagged Valid truncateLSB(csr_instret);

      else if (csr_addr == csr_DCSR)      return tagged Valid 0 ;

      else return tagged Invalid;
   endfunction: fv_read_csr

   // ----------------------------------------------------------------
   // Write a CSR
   // We assume a valid csr_addr, since this is always preceded by a read_csr which performs the check

   function Action fa_write_csr(CSR_Addr csr_addr, Word csr_value);
      action
              if (csr_addr == csr_CYCLE)    csr_cycle   <= zeroExtend(csr_value);

         else if (csr_addr == csr_CYCLEH)
            csr_cycle <= { csr_value[31:0], truncate(csr_cycle) };

         else if (csr_addr == csr_DCSR) begin
            csr_dcsr <= tagged Valid csr_value;
         end

         else begin
            $display("ERROR: RISCV_Spec.fa_write_csr (csr_addr 0x%0h, csr_value 0x%0h): illegal csr_addr", csr_addr, csr_value);
         end
      endaction
   endfunction: fa_write_csr

   // ================================================================
   // Instruction execution

   // ----------------------------------------------------------------
   // The following functions are common idioms for finishing an instruction

   // ----------------
   // Finish exception: record exception cause info, go to ENV_CALL state

   function Action fa_finish_with_exception(Word epc, Exc_Code exc_code, Addr badaddr);
      action
         if ((cfg_verbose != 0) || (exc_code == exc_code_ILLEGAL_INSTRUCTION)) begin
            $display("RISCV(fa_do_exception): epc = 0x%0h, exc_code = 0x%0h, badaddr = 0x%0h", epc, exc_code, badaddr);
         end

         csr_mepc     <= epc;
         csr_mcause   <= { 1'b0, 0, exc_code };
         csr_mbadaddr <= badaddr;

         cpu_state    <= STATE_HALT;
      endaction
   endfunction

   // ----------------
   // Finish instr with no output (no Rd-write): set PC, go to FETCH state

   function Action fa_finish_with_no_output();
      action
         pc        <= fv_fall_through_pc (pc);
         cpu_state <= STATE_FINISH;
      endaction
   endfunction

   // ----------------
   // Finish instr with Rd-write: write Rd, set PC, go to FETCH state

   function Action fa_finish_with_Rd(RegName rd, Word rd_value);
      action
         if (rd != 0) regfile.upd(rd, rd_value);
         pc        <= fv_fall_through_pc(pc);
         cpu_state <= STATE_FINISH;
      endaction
   endfunction

   // ----------------
   // Finish jump instrs; write Rd, set PC, go to FETCH state

   function Action fa_finish_jump(RegName rd, Word rd_value, Addr next_pc);
      action
         if (rd != 0) regfile.upd(rd, rd_value);
         pc        <= next_pc;
         cpu_state <= STATE_FINISH;
      endaction
   endfunction

   // ----------------
   // Finish conditional branch instr: set PC, go to FETCH state

   function Action fa_finish_cond_branch(Bool condition_taken, Addr next_pc);
      action
         pc        <= (condition_taken ? next_pc : fv_fall_through_pc(pc));
         cpu_state <= STATE_FINISH;
      endaction
   endfunction

   // ----------------------------------------------------------------
   // Instruction execution
   // This function encapsulates ALL the opcodes.
   // It has internal functions that group related sub-opcodes.

   function Action fa_exec(Instr instr);
      action
         // ----------------------------------------------------------------
         // Instruction decode

         Decoded_Instr decoded = fv_decode(instr);

         // Values of Rs1 and Rs2 fields of the instr, unsigned
         Word    v1   = ((decoded.rs1 == 0) ? 0: regfile.sub(decoded.rs1));
         Word    v2   = ((decoded.rs2 == 0) ? 0: regfile.sub(decoded.rs2));

         // Values of Rs1 and Rs2 fields of the instr, signed versions
         Word_S  s_v1 = unpack(v1);
         Word_S  s_v2 = unpack(v2);

         // Value of CSR field of instr (if a valid CSR address)
         Maybe #(Word) m_v_csr = fv_read_csr(decoded.csr);

         // ----------------------------------------------------------------
         // Instructions for Upper Immediate

         function Action fa_exec_LUI();
            action
               Bit#(32)    v32   = { decoded.imm20_U, 12'h0 };
               Word_S      iv    = extend(unpack(v32));
               let         value = pack(iv);

               fa_finish_with_Rd(decoded.rd, value);
               if (cfg_verbose > 2) $display("[%7d] Inst: LUI %s, 0x%h", csr_cycle, regNameABI[decoded.rd], value[31:12]);
            endaction
         endfunction: fa_exec_LUI

         function Action fa_exec_AUIPC();
            action
               Word_S  iv    = extend(unpack({ decoded.imm20_U, 12'b0}));
               Word_S  pc_s  = unpack(pc);
               Word    value = pack(pc_s + iv);

               fa_finish_with_Rd(decoded.rd, value);
            endaction
         endfunction: fa_exec_AUIPC

         // ----------------------------------------------------------------
         // Instructions for control-transfer

         function Action fa_exec_JAL();
            action
               Word_S offset  = extend(unpack(decoded.imm21_UJ));
               Addr   next_pc = pack(unpack(pc) + offset);

               fa_finish_jump(decoded.rd, fv_fall_through_pc(pc), next_pc);
            endaction
         endfunction: fa_exec_JAL

         function Action fa_exec_JALR();
            action
               Word_S offset  = extend(unpack (decoded.imm12_I));
               Addr   next_pc = pack(s_v1 + offset);

               fa_finish_jump(decoded.rd, fv_fall_through_pc(pc), next_pc);
            endaction
         endfunction: fa_exec_JALR

         function Action fa_exec_BRANCH();
            action
               Word_S offset  = extend(unpack(decoded.imm13_SB));
               Word   next_pc = pack(unpack(pc) + offset);

               if      (decoded.funct3 == f3_BEQ)  fa_finish_cond_branch(v1  == v2,    next_pc);
               else if (decoded.funct3 == f3_BNE)  fa_finish_cond_branch(v1  != v2,    next_pc);
               else if (decoded.funct3 == f3_BLT)  fa_finish_cond_branch(s_v1 <  s_v2, next_pc);
               else if (decoded.funct3 == f3_BGE)  fa_finish_cond_branch(s_v1 >= s_v2, next_pc);
               else if (decoded.funct3 == f3_BLTU) fa_finish_cond_branch(v1  <  v2,    next_pc);
               else if (decoded.funct3 == f3_BGEU) fa_finish_cond_branch(v1  >= v2,    next_pc);
               else fa_finish_with_exception(pc, exc_code_ILLEGAL_INSTRUCTION, ?);
            endaction
         endfunction: fa_exec_BRANCH

         // ----------------------------------------------------------------
         // LD and ST instructions.
         // Issue request here; will be completed in STATE_EXEC_LD/ST_RESPONSE

         function Action fa_exec_LD_Req();
            action
               Word_S  imm_s    = extend(unpack(decoded.imm12_I));
               Word    mem_addr = pack(s_v1 + imm_s);

               if (cfg_verbose > 1) $display("[%7d] fa_exec_LD_Req: Addr = 0x%08X", csr_cycle, mem_addr);

               function Action fa_LD_Req(Mem_Data_Size sz);
                  action
                     let req = DMem_Req {mem_op:        MEM_OP_LOAD,
                                         mem_data_size: sz,
                                         addr:          mem_addr,
                                         data:          ?};
                     memory.dmem_req(req);
                     cpu_state <= STATE_EXEC_LD_RESPONSE;
                  endaction
               endfunction

               rg_mem_addr <= mem_addr;

               if      (decoded.funct3 == f3_LB)      fa_LD_Req(BITS8);
               else if (decoded.funct3 == f3_LBU)     fa_LD_Req(BITS8);
               else if (decoded.funct3 == f3_LH)      fa_LD_Req(BITS16);
               else if (decoded.funct3 == f3_LHU)     fa_LD_Req(BITS16);
               else if (decoded.funct3 == f3_LW)      fa_LD_Req(BITS32);
               else fa_finish_with_exception(pc, exc_code_ILLEGAL_INSTRUCTION, ?);
            endaction
         endfunction: fa_exec_LD_Req

         function Action fa_exec_ST_Req();
            action
               Word_S  imm_s    = extend(unpack(decoded.imm12_S));
               Word    mem_addr = pack(s_v1 + imm_s);

               function Action fa_ST_req(Mem_Data_Size sz);
                  action
                     let req = DMem_Req {mem_op:        MEM_OP_STORE,
                                         mem_data_size: sz,
                                         addr:          mem_addr,
                                         data:          v2};
                     memory.dmem_req(req);
                     cpu_state <= STATE_EXEC_ST_RESPONSE;
                  endaction
               endfunction

               rg_mem_addr <= mem_addr;

               if      (decoded.funct3 == f3_SB)      fa_ST_req(BITS8);
               else if (decoded.funct3 == f3_SH)      fa_ST_req(BITS16);
               else if (decoded.funct3 == f3_SW)      fa_ST_req(BITS32);
               else fa_finish_with_exception(pc, exc_code_ILLEGAL_INSTRUCTION, ?);
            endaction
         endfunction: fa_exec_ST_Req

         // ----------------------------------------------------------------
         // Instructios for Register-Immediate alu ops

         function Action fa_exec_OP_IMM ();
            action
               Word                v2    = zeroExtend(decoded.imm12_I);
               Word_S              s_v2  = signExtend(unpack(decoded.imm12_I));
               Bit#(TLog#(XLEN))   shamt = truncate(decoded.imm12_I);

               if (cfg_verbose > 2) begin
                  $display("[%7d] Inst: %s %s, %s, 0x%h", csr_cycle,
                        case(decoded.funct3)
                           f3_ADDI: "addi";
                           f3_SLTI: "slti";
                           f3_SLTIU: "sltiu";
                           f3_XORI: "xori";
                           f3_ANDI: "andi";
                           f3_SLLI: "slli";
                           f3_SRxI: ((instr[31:25] == 7'b000_0000) ? "srli" : "srai");
                        endcase,
                        regNameABI[decoded.rd],
                        regNameABI[decoded.rs1],
                        decoded.imm12_I
                  );
               end

               if      (decoded.funct3 == f3_ADDI)  fa_finish_with_Rd(decoded.rd, pack(s_v1 + s_v2));
               else if (decoded.funct3 == f3_SLTI)  fa_finish_with_Rd(decoded.rd, ((s_v1 < s_v2) ? 1 : 0));
               else if (decoded.funct3 == f3_SLTIU) fa_finish_with_Rd(decoded.rd, ((v1  < v2)  ? 1 : 0));
               else if (decoded.funct3 == f3_XORI)  fa_finish_with_Rd(decoded.rd, pack(s_v1 ^ s_v2));
               else if (decoded.funct3 == f3_ORI)   fa_finish_with_Rd(decoded.rd, pack(s_v1 | s_v2));
               else if (decoded.funct3 == f3_ANDI)  fa_finish_with_Rd(decoded.rd, pack(s_v1 & s_v2));

               else if ((decoded.funct3 == f3_SLLI) && (instr[31:25] == 7'b000_0000))
                  fa_finish_with_Rd(decoded.rd, (v1 << shamt));

               // SRLI
               else if ((decoded.funct3 == f3_SRxI) && (instr[31:25] == 7'b000_0000))
                  fa_finish_with_Rd(decoded.rd, (v1 >> shamt));

               // SRAI
               else if ((decoded.funct3 == f3_SRxI) && (instr[31:25] == 7'b010_0000))
                  fa_finish_with_Rd(decoded.rd, pack(s_v1 >> shamt));

               else
                  fa_finish_with_exception(pc, exc_code_ILLEGAL_INSTRUCTION, ?);
            endaction
         endfunction: fa_exec_OP_IMM

         // ----------------------------------------------------------------
         // Instructios for Register-Register alu ops

         function Action fa_exec_OP();
            action
               Bit#(TLog#(XLEN)) shamt = truncate(v2);    // NOTE: upper bits are unspecified in spec

               if      (decoded.funct10 == f10_ADD)  fa_finish_with_Rd(decoded.rd, pack(s_v1 + s_v2));
               else if (decoded.funct10 == f10_SUB)  fa_finish_with_Rd(decoded.rd, pack(s_v1 - s_v2));
               else if (decoded.funct10 == f10_SLL)  fa_finish_with_Rd(decoded.rd, (v1 << shamt));
               else if (decoded.funct10 == f10_SLT)  fa_finish_with_Rd(decoded.rd, ((s_v1 < s_v2) ? 1 : 0));
               else if (decoded.funct10 == f10_SLTU) fa_finish_with_Rd(decoded.rd, ((v1  < v2)  ? 1 : 0));
               else if (decoded.funct10 == f10_XOR)  fa_finish_with_Rd(decoded.rd, pack(s_v1 ^ s_v2));
               else if (decoded.funct10 == f10_SRL)  fa_finish_with_Rd(decoded.rd, (v1 >> shamt));
               else if (decoded.funct10 == f10_SRA)  fa_finish_with_Rd(decoded.rd, pack(s_v1 >> shamt));
               else if (decoded.funct10 == f10_OR)   fa_finish_with_Rd(decoded.rd, pack(s_v1 | s_v2));
               else if (decoded.funct10 == f10_AND)  fa_finish_with_Rd(decoded.rd, pack(s_v1 & s_v2));

               else
                  fa_finish_with_exception(pc, exc_code_ILLEGAL_INSTRUCTION, ?);
            endaction
         endfunction: fa_exec_OP

         // ----------------------------------------------------------------
         // Instructions for MISC-MEM
         // Currently implemented as no-ops (todo: fix)

         function Action fa_exec_MISC_MEM();
            action
               // FENCE
               if (   (decoded.funct3 == f3_FENCE)
                   && (decoded.rd == 0)
                   && (decoded.rs1 == 0)
                   && (instr [31:28] == 4'b0)) begin
                                                  fa_finish_with_no_output;
                                               end

               // FENCE.I
               else if (   (decoded.funct3 == f3_FENCE_I)
                        && (decoded.rd == 0)
                        && (decoded.rs1 == 0)
                        && (decoded.imm12_I == 12'b0)) begin
                                                          fa_finish_with_no_output;
                                                       end

               else begin
                  fa_finish_with_exception(pc, exc_code_ILLEGAL_INSTRUCTION, ?);
               end
            endaction
         endfunction: fa_exec_MISC_MEM

         // ----------------------------------------------------------------
         // Instrucions for System-level ops

         function Action fa_exec_SYSTEM();
            action
               // SCALL/ECALL
               if      (   (decoded.funct3 == f3_PRIV)
                        && (decoded.imm12_I == f12_ECALL)
                        && (decoded.rs1 == 0)
                        && (decoded.rd == 0))
                  fa_finish_with_exception(pc, exc_code_ECALL_FROM_U, ?);

               // SBREAK/EBREAK
               else if (   (decoded.funct3 == f3_PRIV)
                        && (decoded.imm12_I == f12_EBREAK)
                        && (decoded.rs1 == 0)
                        && (decoded.rd == 0))
                  fa_finish_with_exception(pc, exc_code_BREAKPOINT, ?);

               // CSRRW
               else if (m_v_csr matches tagged Valid .csr_old_val &&& (decoded.funct3 == f3_CSRRW)) begin
                  fa_write_csr(decoded.csr, v1);
                  fa_finish_with_Rd(decoded.rd, csr_old_val);
               end

               // CSRRS
               else if (m_v_csr matches tagged Valid .csr_old_val &&& (decoded.funct3 == f3_CSRRS)) begin
                  if (decoded.rs1 != 0) begin
                     Word csr_new_val = (csr_old_val | v1);
                     fa_write_csr(decoded.csr, csr_new_val);
                  end
                  fa_finish_with_Rd(decoded.rd, csr_old_val);
               end

               // CSRRC
               else if (m_v_csr matches tagged Valid .csr_old_val &&& (decoded.funct3 == f3_CSRRC)) begin
                  if (decoded.rs1 != 0) begin
                     Word csr_new_val = (csr_old_val & (~ v1));
                     fa_write_csr(decoded.csr, csr_new_val);
                  end
                  fa_finish_with_Rd(decoded.rd, csr_old_val);
               end

               // CSRRWI
               else if (m_v_csr matches tagged Valid .csr_old_val &&& (decoded.funct3 == f3_CSRRW)) begin
                  let v1 = zeroExtend (decoded.rs1);
                  fa_write_csr(decoded.csr, v1);
                  fa_finish_with_Rd(decoded.rd, csr_old_val);
               end

               // CSRRSI
               else if (m_v_csr matches tagged Valid .csr_old_val &&& (decoded.funct3 == f3_CSRRS)) begin
                  Word v1 = zeroExtend (decoded.rs1);
                  if (v1 != 0) begin
                     Word csr_new_val = (csr_old_val | v1);
                     fa_write_csr(decoded.csr, csr_new_val);
                  end
                  fa_finish_with_Rd(decoded.rd, csr_old_val);
               end

               // CSRRCI
               else if (m_v_csr matches tagged Valid .csr_old_val &&& (decoded.funct3 == f3_CSRRC)) begin
                  Word v1 = zeroExtend (decoded.rs1);
                  if (v1 != 0) begin
                     Word csr_new_val = (csr_old_val & (~ v1));
                     fa_write_csr(decoded.csr, csr_new_val);
                  end
                  fa_finish_with_Rd(decoded.rd, csr_old_val);
               end

               else begin
                  fa_finish_with_exception(pc, exc_code_ILLEGAL_INSTRUCTION, ?);
               end
            endaction
         endfunction: fa_exec_SYSTEM

         // ----------------------------------------------------------------
         // Main body of fa_exec(), dispatching to the sub functions
         // based on major OPCODE

         if (cfg_verbose != 0) $display("[%7d] fa_exec: instr 0x%08h", csr_cycle, instr);

         if      (decoded.opcode == op_LUI)      fa_exec_LUI();
         else if (decoded.opcode == op_AUIPC)    fa_exec_AUIPC();
         else if (decoded.opcode == op_JAL)      fa_exec_JAL();
         else if (decoded.opcode == op_JALR)     fa_exec_JALR();
         else if (decoded.opcode == op_BRANCH)   fa_exec_BRANCH();
         else if (decoded.opcode == op_LOAD)     fa_exec_LD_Req();
         else if (decoded.opcode == op_STORE)    fa_exec_ST_Req();
         else if (decoded.opcode == op_OP_IMM)   fa_exec_OP_IMM();
         else if (decoded.opcode == op_OP)       fa_exec_OP();
         else if (decoded.opcode == op_MISC_MEM) fa_exec_MISC_MEM();
         else if (decoded.opcode == op_SYSTEM)   fa_exec_SYSTEM();
         else fa_finish_with_exception(pc, exc_code_ILLEGAL_INSTRUCTION, ?);
      endaction
   endfunction: fa_exec

   // ================================================================
   // The CPU's top-level logic

   // ---------------- FETCH
   // Issue instruction request

   rule rl_fetch(cpu_state == STATE_FETCH);
      if (cfg_verbose > 1) $display("[%7d] rl_fetch: PC = 0x%08X", csr_cycle, pc);

      memory.imem_req(pc);
      cpu_state <= STATE_EXEC;
   endrule

   // ---------------- EXECUTE
   // Receive instruction from IMem; handle exception if any, else execute it;
   // for LD/ST, issue request and move to LD_RESPONSE/ST_RESPONSE state)

   rule rl_exec(cpu_state == STATE_EXEC);
      let imem_resp <- memory.imem_resp;

      if (imem_resp matches tagged IMem_Resp_Ok .instr) begin
         if (cfg_verbose > 1) $display("[%7d] rl_exec: PC = 0x%08X, instr = 0x%08X", csr_cycle, pc, instr);
         rg_instr <= instr;
         fa_exec(instr);
      end
   endrule

   // ---------------- LD-responses from DMem

   rule rl_exec_LD_response(cpu_state == STATE_EXEC_LD_RESPONSE);
      if (cfg_verbose > 1) $display("[%7d] rl_exec_LD_response: ", csr_cycle);

      Decoded_Instr decoded = fv_decode(rg_instr);

      let resp <- memory.dmem_resp;

      case (resp) matches
         tagged DMem_Resp_Exception .exc_code: fa_finish_with_exception(pc, exc_code, rg_mem_addr);
         tagged DMem_Resp_Ok .u: begin
            case (decoded.funct3)
               f3_LB:  begin
                          Int#(8)   s8    = unpack(truncate(u));
                          Word_S    s     = signExtend(s8);
                          Word      value = pack(s);
                          fa_finish_with_Rd(decoded.rd, value);
                       end
               f3_LBU: begin
                          Bit#(8)   u8    = truncate(u);
                          Word      value = zeroExtend(u8);
                          fa_finish_with_Rd(decoded.rd, value);
                       end
               f3_LH:  begin
                          Int#(16)  s16   = unpack(truncate(u));
                          Word_S    s     = signExtend(s16);
                          Word      value = pack(s);
                          fa_finish_with_Rd(decoded.rd, value);
                       end
               f3_LHU: begin
                          Bit#(16)  u16   = truncate(u);
                          Word      value = zeroExtend(u16);
                          fa_finish_with_Rd(decoded.rd, value);
                       end
               f3_LW:  begin
                          Int#(32)  s32   = unpack(truncate(u));
                          Word_S    s     = signExtend(s32);
                          Word      value = pack(s);
                          fa_finish_with_Rd(decoded.rd, value);
                       end
               // Note: request has already checked that 'default' case is impossible
            endcase
            if (cfg_verbose > 1) $display("RISCV_Spec.rl_exec_LD_response: %08X", u);
         end
      endcase
   endrule

   // ---------------- ST-responses from DMem

   rule rl_exec_ST_response(cpu_state == STATE_EXEC_ST_RESPONSE);
      if (cfg_verbose > 1) $display("[%7d] rl_exec_ST_response: ", csr_cycle);

      DMem_Resp resp <- memory.dmem_resp;

      case (resp) matches
         tagged DMem_Resp_Exception .exc_code: fa_finish_with_exception(pc, exc_code, rg_mem_addr);
         tagged DMem_Resp_Ok        .u:        fa_finish_with_no_output;
      endcase
   endrule

   // ---------------- FINISH: increment csr_instret or record explicit CSRRx update of csr_instret

   rule rl_finish(cpu_state == STATE_FINISH);
      if (cfg_verbose > 1) $display("[%7d] rl_finish: ", csr_cycle);

      csr_instret <= csr_instret + 1;
      cpu_state   <= STATE_FETCH;
   endrule

   // ---------------- Increment csr_cycle according to external oracles

   rule rl_incr_cycle;
      csr_cycle <= csr_cycle + 1;
   endrule

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

   method Action start(Addr initial_pc);
      pc        <= initial_pc;
      cpu_state <= STATE_FETCH;
   endmethod

   method Bool halted;
      return(cpu_state == STATE_HALT);
   endmethod

   method ActionValue#(Word) cpuToHost() if (csr_dcsr matches tagged Valid .ret);
      csr_dcsr <= tagged Invalid;
      return ret;
   endmethod
endmodule

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

(* synthesize *)
module mkRISCV(RISCV_IFC);
   (* hide *) let _m <- _mkRISCV(0);
   return _m;
endmodule

// ================================================================
`ifdef TEST_BENCH_RISCV
module mkTb();
   Reg#(Bit#(32))    cycles <- mkConfigReg(0);

   let               dut         <- mkRISCV;
   Reg#(Bit#(16))    csr_int_low <- mkRegU;

   rule do_cycle;
      cycles <= cycles + 1;
   endrule

   rule do_cpuToHost;
      let csr_value <- dut.cpuToHost;
      Bit#(16) csrCmd = truncateLSB(csr_value);
      Bit#(16) csrDat = truncate(csr_value);

      case(csrCmd)
         0: begin // Exit
            if (csrDat == 0) begin
               $fdisplay(stderr, "PASSED\n");
            end
            else begin
               $fdisplay(stderr, "FAILED: exit code = %d\n", csrDat);
            end
            $finish;
         end
         1: begin // PrintChar
            $fwrite(stderr, "%c", csrDat[7:0]);
         end
         2: begin // PrintIntLow
            csr_int_low <= csrDat;
         end
         3: begin // PrintIntHigh
            $fwrite(stderr, "%d", {csrDat, csr_int_low});
         end
         default: begin
            $fdisplay(stderr, "Unknown type %d", csrCmd);
         end
      endcase
   endrule

   rule do_start(cycles == 0);
      dut.start('h200);
   endrule

   rule do_check(cycles > 0);
      let halted = dut.halted;

      if (halted) begin
         $fdisplay(stderr, "CPU Task Finished");
         $finish;
      end
   endrule
endmodule
`endif // TESTBENCH

// Copyright (c) 2017 Min Chen.  All Rights Reserved.
// Author: Min Chen

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

import RegFile   :: *;    // For RISC-V GPRs
import ConfigReg :: *;

// ================================================================
// BSV project imports

import ISA_Decls :: *;    // Instruction encodings

// ================================================================
// Memory interface for CPU
// ================================================================

import BRAMCore :: *;

// ----------------
// IMem responses: either and exception or an instruction

typedef union tagged {
   Exc_Code  IMem_Resp_Exception;
   Word      IMem_Resp_Ok;
} IMem_Resp deriving(Bits, FShow);

// ----------------
// DMem request ops and sizes

typedef enum {
   MEM_OP_LOAD,
   MEM_OP_STORE
} Mem_Op deriving(Eq, Bits, FShow);

// ----------------
// DMem requests

typedef struct {
   Mem_Op         mem_op;
   Mem_Data_Size  mem_data_size;
   Addr           addr;
   Word           data;          // Only relevant if mem_op == MEM_OP_STORE
} DMem_Req deriving(Bits, FShow);

// ----------------
// DMem responses: either an exception or data
// (data value is only relevant for LOADs, irrelevant for STOREs)

typedef union tagged {
   Exc_Code  DMem_Resp_Exception;
   Word      DMem_Resp_Ok;
} DMem_Resp deriving(Bits, FShow);

// ----------------
// Memory interface reference design

module mkMemory(Memory_IFC);
   BRAM_PORT#(Bit#(14), Word)             imem <- mkBRAMCore1Load(valueOf(TExp#(14)), False, "mem.vmh", False);
   BRAM_DUAL_PORT_BE#(Bit#(15), Word, 4)  dmem <- mkBRAMCore2BELoad(valueOf(TExp#(15)), False, "mem.vmh.D", False);
   Reg#(Bit#(2))                          reg_shift <- mkRegU;
   Reg#(Mem_Data_Size)                    reg_size  <- mkRegU;

   method Action imem_req(Addr addr);
      let phyAddr = addr - imemSt;
      imem.put(False, truncate(phyAddr >> 2), ?);
      //$display("[IMEM] Addr = 0x%08X", addr);
   endmethod

   method ActionValue#(IMem_Resp) imem_resp;
      return tagged IMem_Resp_Ok imem.read();
   endmethod

   method Action dmem_req(DMem_Req req);
      let phyAddr = (req.addr - dmemSt);
      Word val = req.data;
      Bit#(Bits_per_Word_Byte_Index) shift = truncate(req.addr);
      Bit#(Bytes_per_Word) mask = 4'b1111;
      Word pad = ?;

      reg_shift <= shift;
      reg_size <= req.mem_data_size;

      case(req.mem_data_size)
         BITS8: begin
            case(shift)
               0: begin
                  mask = 4'b0001;
               end
               2'd1: begin
                  mask = 4'b0010;
                  val = {pad[15:0], val[7:0], ?};
               end
               2: begin
                  mask = 4'b0100;
                  val = {pad[7:0], val[7:0], ?};
               end
               3: begin
                  mask = 4'b1000;
                  val = {val[7:0], ?};
               end
            endcase
         end
         BITS16: begin
            if (shift[0] != 0) begin
               $display("Unaligned memory access on 16-bits bound");
               $finish;
            end
            if (shift[1] == 0) begin
               mask = 4'b0011;
            end
            else begin
               mask = 4'b1100;
               val = {val[15:0], ?};
            end
         end
      endcase

      dmem.a.put((req.mem_op == MEM_OP_STORE) ? mask : 0, truncate(phyAddr >> 2), val);
      //$display("[DMEM] Addr = 0x%08X", req.addr);
   endmethod

   method ActionValue#(DMem_Resp) dmem_resp;
      Word v = dmem.a.read();

      case(reg_shift)
         0: v = v;
         1: v = {?, v[31: 8]};
         2: v = {?, v[31:16]};
         3: v = {?, v[31:24]};
      endcase

      return tagged DMem_Resp_Ok v;
   endmethod
endmodule

// ----------------------------------------------------------------
// This interface is an argument to the 'mkRISCV_Spec' module,
// and is used insided the module to access memory.
// MMUs, caches etc. are outside this boundary.

interface Memory_IFC;
   method Action                  imem_req(Addr addr);
   method ActionValue#(IMem_Resp) imem_resp;

   method Action                  dmem_req(DMem_Req req);
   method ActionValue#(DMem_Resp) dmem_resp;
endinterface

// ================================================================
// This interfacce is offered by the 'mkRISCV_Spec' module to the environment.
// It is not part of the spec, per se, and just has scaffolding that allows
// the environment to control and CPU and probe its state.

interface RISCV_IFC;
   method Action start(Addr initial_pc);
   method Bool   halted;

   method ActionValue#(Word) cpuToHost;
endinterface

// ================================================================
// The RISC-V CPU Specification module, 'mkRISCV'

// ----------------
// The CPU is initially in the IDLE state.
// It starts running when it is put into the FETCH state.
// When running, it cycles through the following sequences of states:
//
//     FETCH -> EXEC ->                     FINISH    for most instructions
//     FETCH -> EXEC -> EXEC_LD_RESPONSE -> FINISH    for LD instructions
//     FETCH -> EXEC -> EXEC_ST_RESPONSE -> FINISH    for ST instructions
//

typedef enum {STATE_IDLE,
              STATE_FETCH,
              STATE_EXEC,
              STATE_EXEC_LD_RESPONSE,
              STATE_EXEC_ST_RESPONSE,
              STATE_FINISH,
              STATE_HALT
   } CPU_STATE
deriving(Eq, Bits, FShow);

// ----------------
// Default fall-through PC

function Addr fv_fall_through_pc(Addr pc);
   return pc + 4;
endfunction: fv_fall_through_pc

// ----------------

module _mkRISCV#(Bit#(3) cfg_verbose)(RISCV_IFC);

   // internal components
   Memory_IFC           memory   <- mkMemory;

   // Program counter
   Reg#(Word) pc <- mkRegU;

   // General Purpose Registers
   RegFile#(RegName, Word) regfile  <- mkRegFileFull;

   // CSRs
   Reg#(Bit #(64))   csr_cycle   <- mkConfigReg(0);
   Reg#(Bit #(64))   csr_instret <- mkConfigReg(0);

   // ----------------
   // These CSRs are technically not present in the user-mode ISA.

   Reg#(Word)  csr_mepc       <- mkRegU;
   Reg#(Word)  csr_mcause     <- mkRegU;
   Reg#(Word)  csr_mbadaddr   <- mkRegU;

   Reg#(Maybe#(Word)) csr_dcsr <- mkReg(tagged Invalid);

   // ----------------------------------------------------------------
   // Non-architectural state, for this model

   Reg#(CPU_STATE)   cpu_state   <- mkReg(STATE_IDLE);
   Reg#(Addr)        rg_mem_addr <- mkRegU;    // Effective addr in LD/ST
   Reg#(Word)        rg_instr    <- mkRegU;    // Current instruction

   // ----------------------------------------------------------------
   // Read a CSR
   // If the addr is valid, return tagged Valid value
   // else return tagged Invalid

   function Maybe#(Word) fv_read_csr(CSR_Addr csr_addr);
      if      (csr_addr == csr_CYCLE)     return tagged Valid truncate   (csr_cycle);
      else if (csr_addr == csr_INSTRET)   return tagged Valid truncate   (csr_instret);

      else if (csr_addr == csr_CYCLEH  )  return tagged Valid truncateLSB(csr_cycle);
      else if (csr_addr == csr_INSTRETH)  return tagged Valid truncateLSB(csr_instret);

      else if (csr_addr == csr_DCSR)      return tagged Valid 0 ;

      else return tagged Invalid;
   endfunction: fv_read_csr

   // ----------------------------------------------------------------
   // Write a CSR
   // We assume a valid csr_addr, since this is always preceded by a read_csr which performs the check

   function Action fa_write_csr(CSR_Addr csr_addr, Word csr_value);
      action
              if (csr_addr == csr_CYCLE)    csr_cycle   <= zeroExtend(csr_value);

         else if (csr_addr == csr_CYCLEH)
            csr_cycle <= { csr_value[31:0], truncate(csr_cycle) };

         else if (csr_addr == csr_DCSR) begin
            csr_dcsr <= tagged Valid csr_value;
         end

         else begin
            $display("ERROR: RISCV_Spec.fa_write_csr (csr_addr 0x%0h, csr_value 0x%0h): illegal csr_addr", csr_addr, csr_value);
         end
      endaction
   endfunction: fa_write_csr

   // ================================================================
   // Instruction execution

   // ----------------------------------------------------------------
   // The following functions are common idioms for finishing an instruction

   // ----------------
   // Finish exception: record exception cause info, go to ENV_CALL state

   function Action fa_finish_with_exception(Word epc, Exc_Code exc_code, Addr badaddr);
      action
         if ((cfg_verbose != 0) || (exc_code == exc_code_ILLEGAL_INSTRUCTION)) begin
            $display("RISCV(fa_do_exception): epc = 0x%0h, exc_code = 0x%0h, badaddr = 0x%0h", epc, exc_code, badaddr);
         end

         csr_mepc     <= epc;
         csr_mcause   <= { 1'b0, 0, exc_code };
         csr_mbadaddr <= badaddr;

         cpu_state    <= STATE_HALT;
      endaction
   endfunction

   // ----------------
   // Finish instr with no output (no Rd-write): set PC, go to FETCH state

   function Action fa_finish_with_no_output();
      action
         pc        <= fv_fall_through_pc (pc);
         cpu_state <= STATE_FINISH;
      endaction
   endfunction

   // ----------------
   // Finish instr with Rd-write: write Rd, set PC, go to FETCH state

   function Action fa_finish_with_Rd(RegName rd, Word rd_value);
      action
         if (rd != 0) regfile.upd(rd, rd_value);
         pc        <= fv_fall_through_pc(pc);
         cpu_state <= STATE_FINISH;
      endaction
   endfunction

   // ----------------
   // Finish jump instrs; write Rd, set PC, go to FETCH state

   function Action fa_finish_jump(RegName rd, Word rd_value, Addr next_pc);
      action
         if (rd != 0) regfile.upd(rd, rd_value);
         pc        <= next_pc;
         cpu_state <= STATE_FINISH;
      endaction
   endfunction

   // ----------------
   // Finish conditional branch instr: set PC, go to FETCH state

   function Action fa_finish_cond_branch(Bool condition_taken, Addr next_pc);
      action
         pc        <= (condition_taken ? next_pc : fv_fall_through_pc(pc));
         cpu_state <= STATE_FINISH;
      endaction
   endfunction

   // ----------------------------------------------------------------
   // Instruction execution
   // This function encapsulates ALL the opcodes.
   // It has internal functions that group related sub-opcodes.

   function Action fa_exec(Instr instr);
      action
         // ----------------------------------------------------------------
         // Instruction decode

         Decoded_Instr decoded = fv_decode(instr);

         // Values of Rs1 and Rs2 fields of the instr, unsigned
         Word    v1   = ((decoded.rs1 == 0) ? 0: regfile.sub(decoded.rs1));
         Word    v2   = ((decoded.rs2 == 0) ? 0: regfile.sub(decoded.rs2));

         // Values of Rs1 and Rs2 fields of the instr, signed versions
         Word_S  s_v1 = unpack(v1);
         Word_S  s_v2 = unpack(v2);

         // Value of CSR field of instr (if a valid CSR address)
         Maybe #(Word) m_v_csr = fv_read_csr(decoded.csr);

         // ----------------------------------------------------------------
         // Instructions for Upper Immediate

         function Action fa_exec_LUI();
            action
               Bit#(32)    v32   = { decoded.imm20_U, 12'h0 };
               Word_S      iv    = extend(unpack(v32));
               let         value = pack(iv);

               fa_finish_with_Rd(decoded.rd, value);
            endaction
         endfunction: fa_exec_LUI

         function Action fa_exec_AUIPC();
            action
               Word_S  iv    = extend(unpack({ decoded.imm20_U, 12'b0}));
               Word_S  pc_s  = unpack(pc);
               Word    value = pack(pc_s + iv);

               fa_finish_with_Rd(decoded.rd, value);
            endaction
         endfunction: fa_exec_AUIPC

         // ----------------------------------------------------------------
         // Instructions for control-transfer

         function Action fa_exec_JAL();
            action
               Word_S offset  = extend(unpack(decoded.imm21_UJ));
               Addr   next_pc = pack(unpack(pc) + offset);

               fa_finish_jump(decoded.rd, fv_fall_through_pc(pc), next_pc);
            endaction
         endfunction: fa_exec_JAL

         function Action fa_exec_JALR();
            action
               Word_S offset  = extend(unpack (decoded.imm12_I));
               Addr   next_pc = pack(s_v1 + offset);

               fa_finish_jump(decoded.rd, fv_fall_through_pc(pc), next_pc);
            endaction
         endfunction: fa_exec_JALR

         function Action fa_exec_BRANCH();
            action
               Word_S offset  = extend(unpack(decoded.imm13_SB));
               Word   next_pc = pack(unpack(pc) + offset);

               if      (decoded.funct3 == f3_BEQ)  fa_finish_cond_branch(v1  == v2,    next_pc);
               else if (decoded.funct3 == f3_BNE)  fa_finish_cond_branch(v1  != v2,    next_pc);
               else if (decoded.funct3 == f3_BLT)  fa_finish_cond_branch(s_v1 <  s_v2, next_pc);
               else if (decoded.funct3 == f3_BGE)  fa_finish_cond_branch(s_v1 >= s_v2, next_pc);
               else if (decoded.funct3 == f3_BLTU) fa_finish_cond_branch(v1  <  v2,    next_pc);
               else if (decoded.funct3 == f3_BGEU) fa_finish_cond_branch(v1  >= v2,    next_pc);
               else fa_finish_with_exception(pc, exc_code_ILLEGAL_INSTRUCTION, ?);
            endaction
         endfunction: fa_exec_BRANCH

         // ----------------------------------------------------------------
         // LD and ST instructions.
         // Issue request here; will be completed in STATE_EXEC_LD/ST_RESPONSE

         function Action fa_exec_LD_Req();
            action
               Word_S  imm_s    = extend(unpack(decoded.imm12_I));
               Word    mem_addr = pack(s_v1 + imm_s);

               if (cfg_verbose > 1) $display("[%7d] fa_exec_LD_Req: Addr = 0x%08X", csr_cycle, mem_addr);

               function Action fa_LD_Req(Mem_Data_Size sz);
                  action
                     let req = DMem_Req {mem_op:        MEM_OP_LOAD,
                                         mem_data_size: sz,
                                         addr:          mem_addr,
                                         data:          ?};
                     memory.dmem_req(req);
                     cpu_state <= STATE_EXEC_LD_RESPONSE;
                  endaction
               endfunction

               rg_mem_addr <= mem_addr;

               if      (decoded.funct3 == f3_LB)      fa_LD_Req(BITS8);
               else if (decoded.funct3 == f3_LBU)     fa_LD_Req(BITS8);
               else if (decoded.funct3 == f3_LH)      fa_LD_Req(BITS16);
               else if (decoded.funct3 == f3_LHU)     fa_LD_Req(BITS16);
               else if (decoded.funct3 == f3_LW)      fa_LD_Req(BITS32);
               else fa_finish_with_exception(pc, exc_code_ILLEGAL_INSTRUCTION, ?);
            endaction
         endfunction: fa_exec_LD_Req

         function Action fa_exec_ST_Req();
            action
               Word_S  imm_s    = extend(unpack(decoded.imm12_S));
               Word    mem_addr = pack(s_v1 + imm_s);

               function Action fa_ST_req(Mem_Data_Size sz);
                  action
                     let req = DMem_Req {mem_op:        MEM_OP_STORE,
                                         mem_data_size: sz,
                                         addr:          mem_addr,
                                         data:          v2};
                     memory.dmem_req(req);
                     cpu_state <= STATE_EXEC_ST_RESPONSE;
                  endaction
               endfunction

               rg_mem_addr <= mem_addr;

               if      (decoded.funct3 == f3_SB)      fa_ST_req(BITS8);
               else if (decoded.funct3 == f3_SH)      fa_ST_req(BITS16);
               else if (decoded.funct3 == f3_SW)      fa_ST_req(BITS32);
               else fa_finish_with_exception(pc, exc_code_ILLEGAL_INSTRUCTION, ?);
            endaction
         endfunction: fa_exec_ST_Req

         // ----------------------------------------------------------------
         // Instructios for Register-Immediate alu ops

         function Action fa_exec_OP_IMM ();
            action
               Word                v2    = zeroExtend(decoded.imm12_I);
               Word_S              s_v2  = signExtend(unpack(decoded.imm12_I));
               Bit#(TLog#(XLEN))   shamt = truncate(decoded.imm12_I);

               if      (decoded.funct3 == f3_ADDI)  fa_finish_with_Rd(decoded.rd, pack (s_v1 + s_v2));
               else if (decoded.funct3 == f3_SLTI)  fa_finish_with_Rd(decoded.rd, ((s_v1 < s_v2) ? 1 : 0));
               else if (decoded.funct3 == f3_SLTIU) fa_finish_with_Rd(decoded.rd, ((v1  < v2)  ? 1 : 0));
               else if (decoded.funct3 == f3_XORI)  fa_finish_with_Rd(decoded.rd, pack (s_v1 ^ s_v2));
               else if (decoded.funct3 == f3_ORI)   fa_finish_with_Rd(decoded.rd, pack (s_v1 | s_v2));
               else if (decoded.funct3 == f3_ANDI)  fa_finish_with_Rd(decoded.rd, pack (s_v1 & s_v2));

               else if ((decoded.funct3 == f3_SLLI) && (instr[31:25] == 7'b000_0000))
                  fa_finish_with_Rd(decoded.rd, (v1 << shamt));

               // SRLI
               else if ((decoded.funct3 == f3_SRxI) && (instr[31:25] == 7'b000_0000))
                  fa_finish_with_Rd(decoded.rd, (v1 >> shamt));

               // SRAI
               else if ((decoded.funct3 == f3_SRxI) && (instr[31:25] == 7'b010_0000))
                  fa_finish_with_Rd(decoded.rd, pack(s_v1 >> shamt));

               else
                  fa_finish_with_exception(pc, exc_code_ILLEGAL_INSTRUCTION, ?);
            endaction
         endfunction: fa_exec_OP_IMM

         // ----------------------------------------------------------------
         // Instructios for Register-Register alu ops

         function Action fa_exec_OP();
            action
               Bit#(TLog#(XLEN)) shamt = truncate(v2);    // NOTE: upper bits are unspecified in spec

               if      (decoded.funct10 == f10_ADD)  fa_finish_with_Rd(decoded.rd, pack(s_v1 + s_v2));
               else if (decoded.funct10 == f10_SUB)  fa_finish_with_Rd(decoded.rd, pack(s_v1 - s_v2));
               else if (decoded.funct10 == f10_SLL)  fa_finish_with_Rd(decoded.rd, (v1 << shamt));
               else if (decoded.funct10 == f10_SLT)  fa_finish_with_Rd(decoded.rd, ((s_v1 < s_v2) ? 1 : 0));
               else if (decoded.funct10 == f10_SLTU) fa_finish_with_Rd(decoded.rd, ((v1  < v2)  ? 1 : 0));
               else if (decoded.funct10 == f10_XOR)  fa_finish_with_Rd(decoded.rd, pack(s_v1 ^ s_v2));
               else if (decoded.funct10 == f10_SRL)  fa_finish_with_Rd(decoded.rd, (v1 >> shamt));
               else if (decoded.funct10 == f10_SRA)  fa_finish_with_Rd(decoded.rd, pack(s_v1 >> shamt));
               else if (decoded.funct10 == f10_OR)   fa_finish_with_Rd(decoded.rd, pack(s_v1 | s_v2));
               else if (decoded.funct10 == f10_AND)  fa_finish_with_Rd(decoded.rd, pack(s_v1 & s_v2));

               else
                  fa_finish_with_exception(pc, exc_code_ILLEGAL_INSTRUCTION, ?);
            endaction
         endfunction: fa_exec_OP

         // ----------------------------------------------------------------
         // Instructions for MISC-MEM
         // Currently implemented as no-ops (todo: fix)

         function Action fa_exec_MISC_MEM();
            action
               // FENCE
               if (   (decoded.funct3 == f3_FENCE)
                   && (decoded.rd == 0)
                   && (decoded.rs1 == 0)
                   && (instr [31:28] == 4'b0)) begin
                                                  fa_finish_with_no_output;
                                               end

               // FENCE.I
               else if (   (decoded.funct3 == f3_FENCE_I)
                        && (decoded.rd == 0)
                        && (decoded.rs1 == 0)
                        && (decoded.imm12_I == 12'b0)) begin
                                                          fa_finish_with_no_output;
                                                       end

               else begin
                  fa_finish_with_exception(pc, exc_code_ILLEGAL_INSTRUCTION, ?);
               end
            endaction
         endfunction: fa_exec_MISC_MEM

         // ----------------------------------------------------------------
         // Instrucions for System-level ops

         function Action fa_exec_SYSTEM();
            action
               // SCALL/ECALL
               if      (   (decoded.funct3 == f3_PRIV)
                        && (decoded.imm12_I == f12_ECALL)
                        && (decoded.rs1 == 0)
                        && (decoded.rd == 0))
                  fa_finish_with_exception(pc, exc_code_ECALL_FROM_U, ?);

               // SBREAK/EBREAK
               else if (   (decoded.funct3 == f3_PRIV)
                        && (decoded.imm12_I == f12_EBREAK)
                        && (decoded.rs1 == 0)
                        && (decoded.rd == 0))
                  fa_finish_with_exception(pc, exc_code_BREAKPOINT, ?);

               // CSRRW
               else if (m_v_csr matches tagged Valid .csr_old_val &&& (decoded.funct3 == f3_CSRRW)) begin
                  fa_write_csr(decoded.csr, v1);
                  fa_finish_with_Rd(decoded.rd, csr_old_val);
               end

               // CSRRS
               else if (m_v_csr matches tagged Valid .csr_old_val &&& (decoded.funct3 == f3_CSRRS)) begin
                  if (decoded.rs1 != 0) begin
                     Word csr_new_val = (csr_old_val | v1);
                     fa_write_csr(decoded.csr, csr_new_val);
                  end
                  fa_finish_with_Rd(decoded.rd, csr_old_val);
               end

               // CSRRC
               else if (m_v_csr matches tagged Valid .csr_old_val &&& (decoded.funct3 == f3_CSRRC)) begin
                  if (decoded.rs1 != 0) begin
                     Word csr_new_val = (csr_old_val & (~ v1));
                     fa_write_csr(decoded.csr, csr_new_val);
                  end
                  fa_finish_with_Rd(decoded.rd, csr_old_val);
               end

               // CSRRWI
               else if (m_v_csr matches tagged Valid .csr_old_val &&& (decoded.funct3 == f3_CSRRW)) begin
                  let v1 = zeroExtend (decoded.rs1);
                  fa_write_csr(decoded.csr, v1);
                  fa_finish_with_Rd(decoded.rd, csr_old_val);
               end

               // CSRRSI
               else if (m_v_csr matches tagged Valid .csr_old_val &&& (decoded.funct3 == f3_CSRRS)) begin
                  Word v1 = zeroExtend (decoded.rs1);
                  if (v1 != 0) begin
                     Word csr_new_val = (csr_old_val | v1);
                     fa_write_csr(decoded.csr, csr_new_val);
                  end
                  fa_finish_with_Rd(decoded.rd, csr_old_val);
               end

               // CSRRCI
               else if (m_v_csr matches tagged Valid .csr_old_val &&& (decoded.funct3 == f3_CSRRC)) begin
                  Word v1 = zeroExtend (decoded.rs1);
                  if (v1 != 0) begin
                     Word csr_new_val = (csr_old_val & (~ v1));
                     fa_write_csr(decoded.csr, csr_new_val);
                  end
                  fa_finish_with_Rd(decoded.rd, csr_old_val);
               end

               else begin
                  fa_finish_with_exception(pc, exc_code_ILLEGAL_INSTRUCTION, ?);
               end
            endaction
         endfunction: fa_exec_SYSTEM

         // ----------------------------------------------------------------
         // Main body of fa_exec(), dispatching to the sub functions
         // based on major OPCODE

         if (cfg_verbose != 0) $display("[%7d] fa_exec: instr 0x%08h", csr_cycle, instr);

         if      (decoded.opcode == op_LUI)      fa_exec_LUI();
         else if (decoded.opcode == op_AUIPC)    fa_exec_AUIPC();
         else if (decoded.opcode == op_JAL)      fa_exec_JAL();
         else if (decoded.opcode == op_JALR)     fa_exec_JALR();
         else if (decoded.opcode == op_BRANCH)   fa_exec_BRANCH();
         else if (decoded.opcode == op_LOAD)     fa_exec_LD_Req();
         else if (decoded.opcode == op_STORE)    fa_exec_ST_Req();
         else if (decoded.opcode == op_OP_IMM)   fa_exec_OP_IMM();
         else if (decoded.opcode == op_OP)       fa_exec_OP();
         else if (decoded.opcode == op_MISC_MEM) fa_exec_MISC_MEM();
         else if (decoded.opcode == op_SYSTEM)   fa_exec_SYSTEM();
         else fa_finish_with_exception(pc, exc_code_ILLEGAL_INSTRUCTION, ?);
      endaction
   endfunction: fa_exec

   // ================================================================
   // The CPU's top-level logic

   // ---------------- FETCH
   // Issue instruction request

   rule rl_fetch(cpu_state == STATE_FETCH);
      if (cfg_verbose > 1) $display("[%7d] rl_fetch: PC = 0x%08X", csr_cycle, pc);

      memory.imem_req(pc);
      cpu_state <= STATE_EXEC;
   endrule

   // ---------------- EXECUTE
   // Receive instruction from IMem; handle exception if any, else execute it;
   // for LD/ST, issue request and move to LD_RESPONSE/ST_RESPONSE state)

   rule rl_exec(cpu_state == STATE_EXEC);
      let imem_resp <- memory.imem_resp;

      case (imem_resp) matches
         // IMem response is an exception
         tagged IMem_Resp_Exception .exc_code:
            begin
               fa_finish_with_exception(pc, exc_code, pc);
            end

         // IMem response is ok
         tagged IMem_Resp_Ok  .instr:
            begin
               if (cfg_verbose > 1) $display("[%7d] rl_exec: PC = 0x%08X, instr = 0x%08X", csr_cycle, pc, instr);
               rg_instr <= instr;
               fa_exec(instr);
            end
      endcase
   endrule

   // ---------------- LD-responses from DMem

   rule rl_exec_LD_response(cpu_state == STATE_EXEC_LD_RESPONSE);
      if (cfg_verbose > 1) $display("[%7d] rl_exec_LD_response: ", csr_cycle);

      Decoded_Instr decoded = fv_decode(rg_instr);

      let resp <- memory.dmem_resp;

      case (resp) matches
         tagged DMem_Resp_Exception .exc_code: fa_finish_with_exception(pc, exc_code, rg_mem_addr);
         tagged DMem_Resp_Ok .u: begin
            case (decoded.funct3)
               f3_LB:  begin
                          Int#(8)   s8    = unpack(truncate(u));
                          Word_S    s     = signExtend(s8);
                          Word      value = pack(s);
                          fa_finish_with_Rd(decoded.rd, value);
                       end
               f3_LBU: begin
                          Bit#(8)   u8    = truncate(u);
                          Word      value = zeroExtend(u8);
                          fa_finish_with_Rd(decoded.rd, value);
                       end
               f3_LH:  begin
                          Int#(16)  s16   = unpack(truncate(u));
                          Word_S    s     = signExtend(s16);
                          Word      value = pack(s);
                          fa_finish_with_Rd(decoded.rd, value);
                       end
               f3_LHU: begin
                          Bit#(16)  u16   = truncate(u);
                          Word      value = zeroExtend(u16);
                          fa_finish_with_Rd(decoded.rd, value);
                       end
               f3_LW:  begin
                          Int#(32)  s32   = unpack(truncate(u));
                          Word_S    s     = signExtend(s32);
                          Word      value = pack(s);
                          fa_finish_with_Rd(decoded.rd, value);
                       end
               // Note: request has already checked that 'default' case is impossible
            endcase
            if (cfg_verbose > 1) $display("RISCV_Spec.rl_exec_LD_response: %08X", u);
         end
      endcase
   endrule

   // ---------------- ST-responses from DMem

   rule rl_exec_ST_response(cpu_state == STATE_EXEC_ST_RESPONSE);
      if (cfg_verbose > 1) $display("[%7d] rl_exec_ST_response: ", csr_cycle);

      DMem_Resp resp <- memory.dmem_resp;

      case (resp) matches
         tagged DMem_Resp_Exception .exc_code: fa_finish_with_exception(pc, exc_code, rg_mem_addr);
         tagged DMem_Resp_Ok        .u:        fa_finish_with_no_output;
      endcase
   endrule

   // ---------------- FINISH: increment csr_instret or record explicit CSRRx update of csr_instret

   rule rl_finish(cpu_state == STATE_FINISH);
      if (cfg_verbose > 1) $display("[%7d] rl_finish: ", csr_cycle);

      csr_instret <= csr_instret + 1;
      cpu_state   <= STATE_FETCH;
   endrule

   // ---------------- Increment csr_cycle according to external oracles

   rule rl_incr_cycle;
      csr_cycle <= csr_cycle + 1;
   endrule

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

   method Action start(Addr initial_pc);
      pc        <= initial_pc;
      cpu_state <= STATE_FETCH;
   endmethod

   method Bool halted;
      return(cpu_state == STATE_HALT);
   endmethod

   method ActionValue#(Word) cpuToHost() if (csr_dcsr matches tagged Valid .ret);
      csr_dcsr <= tagged Invalid;
      return ret;
   endmethod
endmodule

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

(* synthesize *)
module mkRISCV(RISCV_IFC);
   (* hide *) let _m <- _mkRISCV(0);
   return _m;
endmodule

// ================================================================
`ifdef TEST_BENCH_RISCV
module mkTb();
   Reg#(Bit#(32))    cycles <- mkConfigReg(0);

   let               dut         <- mkRISCV;
   Reg#(Bit#(16))    csr_int_low <- mkRegU;

   rule do_cycle;
      cycles <= cycles + 1;
   endrule

   rule do_cpuToHost;
      let csr_value <- dut.cpuToHost;
      Bit#(16) csrCmd = truncateLSB(csr_value);
      Bit#(16) csrDat = truncate(csr_value);

      case(csrCmd)
         0: begin // Exit
            if (csrDat == 0) begin
               $fdisplay(stderr, "PASSED\n");
            end
            else begin
               $fdisplay(stderr, "FAILED: exit code = %d\n", csrDat);
            end
            $finish;
         end
         1: begin // PrintChar
            $fwrite(stderr, "%c", csrDat[7:0]);
         end
         2: begin // PrintIntLow
            csr_int_low <= csrDat;
         end
         3: begin // PrintIntHigh
            $fwrite(stderr, "%d", {csrDat, csr_int_low});
         end
         default: begin
            $fdisplay(stderr, "Unknown type %d", csrCmd);
         end
      endcase
   endrule

   rule do_start(cycles == 0);
      dut.start('h200);
   endrule

   rule do_check(cycles > 0);
      let halted = dut.halted;

      if (halted) begin
         $fdisplay(stderr, "CPU Task Finished");
         $finish;
      end
   endrule
endmodule
`endif // TESTBENCH