Tab reformatting in RFile.

Tab reformatting in RFile.

import RegFile::*;
import Instructions::*;

interface RFile;
    method Action wr(Ridx ridx, Bit#(32) data);
    method Bit#(32) rd1(Ridx ridx);
    method Bit#(32) rd2(Ridx ridx);
endinterface

module mkRFile(RFile);
    RegFile#(Ridx,Bit#(32)) rf <- mkRegFileWCF(0,31);
    RWire#(Tuple2#(Ridx,Bit#(32))) rwout <- mkRWire;
    method Action wr(Ridx ridx, Bit#(32) data);
        rf.upd(ridx, data);
        rwout.wset(tuple2(ridx, data));
    endmethod
    method Bit#(32) rd1(Ridx ridx);
        case (rwout.wget) matches
            tagged Valid {.wr,.d}: return (ridx == 0) ? 0 : (wr==ridx) ? d : rf.sub(ridx);
            tagged Invalid: return (ridx == 0) ? 0 : rf.sub(ridx);
        endcase
    endmethod
    method Bit#(32) rd2(Ridx ridx);
        case (rwout.wget) matches
            tagged Valid {.wr,.d}: return (ridx == 0) ? 0 : (wr==ridx) ? d : rf.sub(ridx);
            tagged Invalid: return (ridx == 0) ? 0 : rf.sub(ridx);
        endcase
    endmethod
endmodule


Check giga page wrong

Check giga page wrong

// Copyright (c) 2017 Massachusetts Institute of Technology
//
// 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.

`include "ProcConfig.bsv"

import Vector::*;
import Types::*;
import ProcTypes::*;

// processor req/resp with I/D TLB
typedef struct{
    Addr  addr;
    Bool  write;
} TlbReq deriving(Eq, Bits, FShow);
typedef Tuple2#(Addr, Maybe#(Exception)) TlbResp;

// non-blocking DTLB
typedef `DTLB_REQ_NUM DTlbReqNum;
typedef Bit#(TLog#(DTlbReqNum)) DTlbReqIdx;

// non-blocking L2 TLB
typedef `L2TLB_REQ_NUM L2TlbReqNum;
typedef Bit#(TLog#(L2TlbReqNum)) L2TlbReqIdx;

// Only for Sv39
typedef 27 VpnSz;
typedef Bit#(VpnSz) Vpn;
typedef 44 PpnSz;
typedef Bit#(PpnSz) Ppn;
typedef 12 PageOffsetSz; // 4KB basic page
typedef Bit#(PageOffsetSz) PageOffset;
typedef 9 VpnIdxSz; // Vpn is broken down to 3 indexes to 3 levels of page table
typedef Bit#(VpnIdxSz) VpnIdx;
typedef Bit#(2) PageWalkLevel; // 2: 1GB page, 1: 2MB page, 0: 4KB page
typedef 3 NumPageWalkLevels;
PageWalkLevel maxPageWalkLevel = fromInteger(valueof(NumPageWalkLevels) - 1);

typedef struct {
    Bool dirty;
    Bool accessed;
    Bool global;
    Bool user;
    Bool executable;
    Bool writable;
    Bool readable;
} PTEType deriving (Bits, Eq, FShow);

typedef struct {
    Bit#(10) reserved;
    Ppn ppn;
    Bit#(2) reserved_sw; // reserved for supervisor software
    PTEType pteType;
    Bool valid;
} PTESv39 deriving(Bits, Eq, FShow);

// TLB entry
typedef struct {
    Vpn           vpn;
    Ppn           ppn;
    PTEType       pteType;
    PageWalkLevel level;
    Asid          asid;
} TlbEntry deriving (Bits, Eq, FShow);

// SV39 translate
function Vpn getVpn(Addr addr) = addr[38:12];

function PageOffset getPageOffset(Addr addr) = truncate(addr);

function Addr getPTBaseAddr(Ppn basePpn);
    PageOffset offset = 0;
    return zeroExtend({basePpn, offset});
endfunction

`ifdef SECURITY
// Check if an access from enclave or security monitor is out of its protection
// domain. This should always return false for normal program or OS, becuase in
// vminfo of OS/normal program, evbase = 0xfff...fff and evmask = 0
function Bool outOfProtectionDomain(VMInfo vm_info, Addr vaddr);
    // Default value for when the program requiring the translation is not protected
    if (vm_info.sanctum_evbase == maxBound && vm_info.sanctum_evmask == 0) return False;
    // If it is protected, then the size of the protection domain is a power of
    // 2 starting at sanctum_evbase
    else return ((vaddr & vm_info.sanctum_evmask) != vm_info.sanctum_evbase );
endfunction

// get the bitmask for accessed DRAM regions
// FIXME This code assumes 64 x 32MB regions
function Addr getAddrRegions(Addr addr, Bool isLeaf, PageWalkLevel level) provisos (
    Add#(0, 6, `LOG_DRAM_REGION_NUM), // 6 regions
    Add#(0, 25, `LOG_DRAM_REGION_SIZE) // 25 regions
);
    Addr res = (1 << (addr[30:25]));
    if (isLeaf && (level == 2)) begin // giga pages cross multiple regions
        res = ((addr[31] == 1'b1) ? 64'hFFFFFFFF00000000 : 64'h00000000FFFFFFFF); // Assume 64 x 32-MB regions
    end
    return res;
endfunction
`endif

function Addr getPTEAddr(Addr baseAddr, Vpn vpn, PageWalkLevel level);
    Vector#(NumPageWalkLevels, VpnIdx) vpnVec = unpack(vpn); // index 0 is LSB
    return baseAddr + (zeroExtend(vpnVec[level]) << 3); // PTE is 2^3 bytes
endfunction

function Bool isLeafPTE(PTEType t);
    return t.executable || t.readable || t.writable;
endfunction

function Addr translate(Addr addr, Ppn ppn, PageWalkLevel level);
    return zeroExtend(case (level)
        0: {ppn, getPageOffset(addr)}; // 4KB page
        1: {ppn[43:9], addr[20:0]};   // 2MB page
        2: {ppn[43:18], addr[29:0]};  // 1GB page
        default: 0; // should not happen
    endcase);
endfunction

function Vpn getMaskedVpn(Vpn vpn, PageWalkLevel level);
    return (case (level)
        0: (vpn);
        1: ((vpn >> 9) << 9);   // 2MB mask
        2: ((vpn >> 18) << 18); // 1GB mask
        default: 0; // should not happen
    endcase);
endfunction

function Ppn getMaskedPpn(Ppn ppn, PageWalkLevel level);
    return (case (level)
        0: (ppn);
        1: ((ppn >> 9) << 9);   // 2MB mask
        2: ((ppn >> 18) << 18); // 1GB mask
        default: 0; // should not happen
    endcase);
endfunction

function Bool isPpnAligned(Ppn ppn, PageWalkLevel level);
    return (case(level)
        0: True;
        1: (ppn[8:0] == 0);
        2: (ppn[17:0] == 0);
        default: False;
    endcase);
endfunction

typedef enum {
    InstFetch,
    DataLoad,
    DataStore // also contain DataLoad
} TlbAccessType deriving(Bits, Eq, FShow);

function Bool hasVMPermission(
    VMInfo vm_info,
    PTEType pte_type, Ppn ppn, PageWalkLevel level,
    TlbAccessType access
);
    // try to find any page fault
    Bool fault = False;

    // check if we are still in sv39
    if(!vm_info.sv39) begin
        fault = True;
    end

    // check PTE itself is well-formed or not
    if(pte_type.writable && !pte_type.readable) begin
        fault = True; // page writable but not readable
    end
    if(!isPpnAligned(ppn, level)) begin
        fault = True; // unaligned super page
    end

    // check permission related to user page
    if(pte_type.user) begin
        // S mode may not access user page. We need to consider mstatus.sum
        // bit. XXX Spike will raise page fault in case S-mode inst-fetch even
        // when mstatus.sum is set. We follow spike here.
        if (vm_info.prv == prvS &&
            (access == InstFetch || !vm_info.userAccessibleByS)) begin
            fault = True;
        end
    end
    else begin
        // U mode cannot access non-user page
        if(vm_info.prv == prvU) begin
            fault = True;
        end
    end

    // check execute/read/write permission
    case(access)
        InstFetch: begin
            if(!pte_type.executable) begin
                fault = True;
            end
        end
        DataLoad: begin
            // need to consider mstatus.mxr bit
            if (!pte_type.readable &&
                !(pte_type.executable && vm_info.exeReadable)) begin
                fault = True;
            end
        end
        DataStore: begin
            // store requires page to be both readable and writable
            if(!(pte_type.readable && pte_type.writable)) begin
                fault = True;
            end
        end
    endcase

    // check if accessed or dirty bit needs to be set
    if(!pte_type.accessed) begin
        fault = True;
    end
    if(access == DataStore && !pte_type.dirty) begin
        fault = True;
    end

    return !fault;
endfunction

// Copyright (c) 2017 Massachusetts Institute of Technology
//
// 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.

`include "ProcConfig.bsv"

import Vector::*;
import Types::*;
import ProcTypes::*;

// processor req/resp with I/D TLB
typedef struct{
    Addr  addr;
    Bool  write;
} TlbReq deriving(Eq, Bits, FShow);
typedef Tuple2#(Addr, Maybe#(Exception)) TlbResp;

// non-blocking DTLB
typedef `DTLB_REQ_NUM DTlbReqNum;
typedef Bit#(TLog#(DTlbReqNum)) DTlbReqIdx;

// non-blocking L2 TLB
typedef `L2TLB_REQ_NUM L2TlbReqNum;
typedef Bit#(TLog#(L2TlbReqNum)) L2TlbReqIdx;

// Only for Sv39
typedef 27 VpnSz;
typedef Bit#(VpnSz) Vpn;
typedef 44 PpnSz;
typedef Bit#(PpnSz) Ppn;
typedef 12 PageOffsetSz; // 4KB basic page
typedef Bit#(PageOffsetSz) PageOffset;
typedef 9 VpnIdxSz; // Vpn is broken down to 3 indexes to 3 levels of page table
typedef Bit#(VpnIdxSz) VpnIdx;
typedef Bit#(2) PageWalkLevel; // 2: 1GB page, 1: 2MB page, 0: 4KB page
typedef 3 NumPageWalkLevels;
PageWalkLevel maxPageWalkLevel = fromInteger(valueof(NumPageWalkLevels) - 1);

typedef struct {
    Bool dirty;
    Bool accessed;
    Bool global;
    Bool user;
    Bool executable;
    Bool writable;
    Bool readable;
} PTEType deriving (Bits, Eq, FShow);

typedef struct {
    Bit#(10) reserved;
    Ppn ppn;
    Bit#(2) reserved_sw; // reserved for supervisor software
    PTEType pteType;
    Bool valid;
} PTESv39 deriving(Bits, Eq, FShow);

// TLB entry
typedef struct {
    Vpn           vpn;
    Ppn           ppn;
    PTEType       pteType;
    PageWalkLevel level;
    Asid          asid;
} TlbEntry deriving (Bits, Eq, FShow);

// SV39 translate
function Vpn getVpn(Addr addr) = addr[38:12];

function PageOffset getPageOffset(Addr addr) = truncate(addr);

function Addr getPTBaseAddr(Ppn basePpn);
    PageOffset offset = 0;
    return zeroExtend({basePpn, offset});
endfunction

`ifdef SECURITY
// Check if an access from enclave or security monitor is out of its protection
// domain. This should always return false for normal program or OS, becuase in
// vminfo of OS/normal program, evbase = 0xfff...fff and evmask = 0
function Bool outOfProtectionDomain(VMInfo vm_info, Addr vaddr);
    // Default value for when the program requiring the translation is not protected
    if (vm_info.sanctum_evbase == maxBound && vm_info.sanctum_evmask == 0) return False;
    // If it is protected, then the size of the protection domain is a power of
    // 2 starting at sanctum_evbase
    else return ((vaddr & vm_info.sanctum_evmask) != vm_info.sanctum_evbase );
endfunction

// get the bitmask for accessed DRAM regions
// FIXME This code assumes 64 x 32MB regions
function Addr getAddrRegions(Addr addr, Bool isLeaf, PageWalkLevel level) provisos (
    Add#(0, 6, `LOG_DRAM_REGION_NUM), // 6 regions
    Add#(0, 25, `LOG_DRAM_REGION_SIZE) // 25 regions
);
    Addr res = (1 << (addr[30:25]));
    if (isLeaf && (level == 2)) begin // giga pages cross multiple regions
        res = ((addr[30] == 1'b1) ? 64'hFFFFFFFF00000000 : 64'h00000000FFFFFFFF); // Assume 64 x 32-MB regions
    end
    return res;
endfunction
`endif

function Addr getPTEAddr(Addr baseAddr, Vpn vpn, PageWalkLevel level);
    Vector#(NumPageWalkLevels, VpnIdx) vpnVec = unpack(vpn); // index 0 is LSB
    return baseAddr + (zeroExtend(vpnVec[level]) << 3); // PTE is 2^3 bytes
endfunction

function Bool isLeafPTE(PTEType t);
    return t.executable || t.readable || t.writable;
endfunction

function Addr translate(Addr addr, Ppn ppn, PageWalkLevel level);
    return zeroExtend(case (level)
        0: {ppn, getPageOffset(addr)}; // 4KB page
        1: {ppn[43:9], addr[20:0]};   // 2MB page
        2: {ppn[43:18], addr[29:0]};  // 1GB page
        default: 0; // should not happen
    endcase);
endfunction

function Vpn getMaskedVpn(Vpn vpn, PageWalkLevel level);
    return (case (level)
        0: (vpn);
        1: ((vpn >> 9) << 9);   // 2MB mask
        2: ((vpn >> 18) << 18); // 1GB mask
        default: 0; // should not happen
    endcase);
endfunction

function Ppn getMaskedPpn(Ppn ppn, PageWalkLevel level);
    return (case (level)
        0: (ppn);
        1: ((ppn >> 9) << 9);   // 2MB mask
        2: ((ppn >> 18) << 18); // 1GB mask
        default: 0; // should not happen
    endcase);
endfunction

function Bool isPpnAligned(Ppn ppn, PageWalkLevel level);
    return (case(level)
        0: True;
        1: (ppn[8:0] == 0);
        2: (ppn[17:0] == 0);
        default: False;
    endcase);
endfunction

typedef enum {
    InstFetch,
    DataLoad,
    DataStore // also contain DataLoad
} TlbAccessType deriving(Bits, Eq, FShow);

function Bool hasVMPermission(
    VMInfo vm_info,
    PTEType pte_type, Ppn ppn, PageWalkLevel level,
    TlbAccessType access
);
    // try to find any page fault
    Bool fault = False;

    // check if we are still in sv39
    if(!vm_info.sv39) begin
        fault = True;
    end

    // check PTE itself is well-formed or not
    if(pte_type.writable && !pte_type.readable) begin
        fault = True; // page writable but not readable
    end
    if(!isPpnAligned(ppn, level)) begin
        fault = True; // unaligned super page
    end

    // check permission related to user page
    if(pte_type.user) begin
        // S mode may not access user page. We need to consider mstatus.sum
        // bit. XXX Spike will raise page fault in case S-mode inst-fetch even
        // when mstatus.sum is set. We follow spike here.
        if (vm_info.prv == prvS &&
            (access == InstFetch || !vm_info.userAccessibleByS)) begin
            fault = True;
        end
    end
    else begin
        // U mode cannot access non-user page
        if(vm_info.prv == prvU) begin
            fault = True;
        end
    end

    // check execute/read/write permission
    case(access)
        InstFetch: begin
            if(!pte_type.executable) begin
                fault = True;
            end
        end
        DataLoad: begin
            // need to consider mstatus.mxr bit
            if (!pte_type.readable &&
                !(pte_type.executable && vm_info.exeReadable)) begin
                fault = True;
            end
        end
        DataStore: begin
            // store requires page to be both readable and writable
            if(!(pte_type.readable && pte_type.writable)) begin
                fault = True;
            end
        end
    endcase

    // check if accessed or dirty bit needs to be set
    if(!pte_type.accessed) begin
        fault = True;
    end
    if(access == DataStore && !pte_type.dirty) begin
        fault = True;
    end

    return !fault;
endfunction


updated calls to $display

updated calls to $display

// 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 Vector::*;
import BuildVector::*;
import FIFOF::*;
import FIFO::*;
import BRAMFIFO::*;
import GetPut::*;
import ClientServer::*;

import ConnectalMemory::*;
import MemTypes::*;
import MemreadEngine::*;
import MemwriteEngine::*;
import Pipe::*;

interface MemcpyRequest;
   method Action startCopy(Bit#(32) wrPointer, Bit#(32) rdPointer, Bit#(32) numWords, Bit#(32) burstLen, Bit#(32) iterCnt);
endinterface

interface MemcpyIndication;
   method Action started;
   method Action done;
endinterface

interface Memcpy;
   interface MemcpyRequest request;
   interface Vector#(1, MemReadClient#(64)) dmaReadClient;
   interface Vector#(1, MemWriteClient#(64)) dmaWriteClient;
endinterface

// NOTE: this test doesn't rely on mkDma[Read|Write]Buffer to ensure that
//       speculative read/write requests are not unsafely issued.  As a
//       result this must be enforced manually (mdk)

module mkMemcpy#(MemcpyIndication indication)(Memcpy);

   MemreadEngine#(64,1,1)  re <- mkMemreadEngine;
   MemwriteEngine#(64,2,1) we <- mkMemwriteEngine;

   Reg#(Bit#(32))        rdIterCnt <- mkReg(0);
   Reg#(Bit#(32))        wrIterCnt <- mkReg(0);
   Reg#(Bit#(32))            rdCnt <- mkReg(0);
   Reg#(Bit#(32))            wrCnt <- mkReg(0);
   Reg#(SGLId)      rdPointer <- mkReg(0);
   Reg#(SGLId)      wrPointer <- mkReg(0);
   Reg#(Bit#(32))         burstLen <- mkReg(0);
   Reg#(Bit#(32))         numWords <- mkReg(0);

   FIFOF#(Bit#(64))    buffer <- mkSizedBRAMFIFOF(16);
   Reg#(Bit#(32))    rdBuffer <- mkReg(32);
   Reg#(Bit#(32))    wrBuffer <- mkReg(0);

   Bool verbose = True;

   rule start_read(rdIterCnt > 0 && rdBuffer >= burstLen);
      if (verbose) $display("start_read %d", rdCnt);
      re.readServers[0].request.put(MemengineCmd{sglId:rdPointer, base:extend(rdCnt*4), len:(burstLen*4), burstLen:truncate(burstLen*4)});
      rdBuffer <= rdBuffer-burstLen;
      if(rdCnt+burstLen >= numWords) begin
	 rdCnt <= 0;
	 rdIterCnt <= rdIterCnt-1;
      end
      else begin
	 rdCnt <= rdCnt+burstLen;
      end
   endrule

   rule start_write(wrIterCnt > 0 && wrBuffer >= burstLen);
      if (verbose) $display("                    start_write %d", wrCnt);
      we.writeServers[0].request.put(MemengineCmd{sglId:wrPointer, base:extend(wrCnt*4), len:burstLen*4, burstLen:truncate(burstLen*4)});
      wrBuffer <= wrBuffer-burstLen;
      if(wrCnt+burstLen >= numWords) begin
	 wrCnt <= 0;
	 wrIterCnt <= wrIterCnt-1;
      end
      else begin
	 wrCnt <= wrCnt+burstLen;
      end
   endrule

   rule read_finish;
      if (verbose) $display("read_finish %d", rdIterCnt);
      let rv0 <- re.readServers[0].response.get;
   endrule

   rule write_finish;
      if (verbose) $display("                    write_finish %d", wrIterCnt);
      let rv1 <- we.writeServers[0].response.get;
      if(wrIterCnt==0)
	 indication.done;
   endrule

   rule fill_buffer;
      let v <- toGet(re.dataPipes[0]).get;
      buffer.enq(v);
      wrBuffer <= wrBuffer+2;
      if (verbose) $display("fill_buffer %h", v);
   endrule

   rule drain_buffer;
      let v <- toGet(buffer).get();
      we.dataPipes[0].enq(v);
      rdBuffer <= rdBuffer+2;
      //$display("                    drain_buffer %h", buffer.first);
   endrule

   interface MemcpyRequest request;
   method Action startCopy(Bit#(32) wp, Bit#(32) rp, Bit#(32) nw, Bit#(32) bl, Bit#(32) ic);
      $display("startCopy wrPointer=%d rdPointer=%d numWords=%h burstLen=%d iterCnt=%d", wp, rp, nw, bl, ic);
      indication.started;
      // initialized
      wrPointer <= wp;
      rdPointer <= rp;
      numWords  <= nw;
      wrIterCnt <= ic;
      rdIterCnt <= ic;
      burstLen  <= bl;
   endmethod
   endinterface
   interface MemReadClient dmaReadClient = vec(re.dmaClient);
   interface MemWriteClient dmaWriteClient = vec(we.dmaClient);
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 Vector::*;
import FIFOF::*;
import FIFO::*;
import BRAMFIFO::*;
import GetPut::*;
import ClientServer::*;

import ConnectalMemory::*;
import MemTypes::*;
import MemreadEngine::*;
import MemwriteEngine::*;
import Pipe::*;

interface MemcpyRequest;
   method Action startCopy(Bit#(32) wrPointer, Bit#(32) rdPointer, Bit#(32) numWords, Bit#(32) burstLen, Bit#(32) iterCnt);
endinterface

interface MemcpyIndication;
   method Action started;
   method Action done;
endinterface

interface Memcpy;
   interface MemcpyRequest request;
   interface Vector#(1, MemReadClient#(64)) dmaReadClient;
   interface Vector#(1, MemWriteClient#(64)) dmaWriteClient;
endinterface

// NOTE: this test doesn't rely on mkDma[Read|Write]Buffer to ensure that
//       speculative read/write requests are not unsafely issued.  As a
//       result this must be enforced manually (mdk)

module mkMemcpy#(MemcpyIndication indication)(Memcpy);

   MemreadEngine#(64,1,1)  re <- mkMemreadEngine;
   MemwriteEngine#(64,2,1) we <- mkMemwriteEngine;

   Reg#(Bit#(32))        rdIterCnt <- mkReg(0);
   Reg#(Bit#(32))        wrIterCnt <- mkReg(0);
   Reg#(Bit#(32))            rdCnt <- mkReg(0);
   Reg#(Bit#(32))            wrCnt <- mkReg(0);
   Reg#(SGLId)      rdPointer <- mkReg(0);
   Reg#(SGLId)      wrPointer <- mkReg(0);
   Reg#(Bit#(32))         burstLen <- mkReg(0);
   Reg#(Bit#(32))         numWords <- mkReg(0);

   FIFOF#(Bit#(64))    buffer <- mkSizedBRAMFIFOF(16);
   Reg#(Bit#(32))    rdBuffer <- mkReg(32);
   Reg#(Bit#(32))    wrBuffer <- mkReg(0);

   rule start_read(rdIterCnt > 0 && rdBuffer >= burstLen);
      //$display("start_read %d", rdCnt);
      re.readServers[0].request.put(MemengineCmd{sglId:rdPointer, base:extend(rdCnt*4), len:(burstLen*4), burstLen:truncate(burstLen*4)});
      rdBuffer <= rdBuffer-burstLen;
      if(rdCnt+burstLen >= numWords) begin
	 rdCnt <= 0;
	 rdIterCnt <= rdIterCnt-1;
      end
      else begin
	 rdCnt <= rdCnt+burstLen;
      end
   endrule

   rule start_write(wrIterCnt > 0 && wrBuffer >= burstLen);
      //$display("                    start_write %d", wrCnt);
      we.writeServers[0].request.put(MemengineCmd{sglId:wrPointer, base:extend(wrCnt*4), len:burstLen*4, burstLen:truncate(burstLen*4)});
      wrBuffer <= wrBuffer-burstLen;
      if(wrCnt+burstLen >= numWords) begin
	 wrCnt <= 0;
	 wrIterCnt <= wrIterCnt-1;
      end
      else begin
	 wrCnt <= wrCnt+burstLen;
      end
   endrule

   rule read_finish;
      //$display("read_finish %d", rdIterCnt);
      let rv0 <- re.readServers[0].response.get;
   endrule

   rule write_finish;
      //$display("                    write_finish %d", wrIterCnt);
      let rv1 <- we.writeServers[0].response.get;
      if(wrIterCnt==0)
	 indication.done;
   endrule

   rule fill_buffer;
      let v <- toGet(re.dataPipes[0]).get;
      buffer.enq(v);
      wrBuffer <= wrBuffer+2;
      //$display("fill_buffer %h", rdFifo.first);
   endrule

   rule drain_buffer;
      let v <- toGet(buffer).get();
      we.dataPipes[0].enq(v);
      rdBuffer <= rdBuffer+2;
      //$display("                    drain_buffer %h", buffer.first);
   endrule

   interface MemcpyRequest request;
   method Action startCopy(Bit#(32) wp, Bit#(32) rp, Bit#(32) nw, Bit#(32) bl, Bit#(32) ic);
      $display("startCopy wrPointer=%d rdPointer=%d numWords=%h burstLen=%d iterCnt=%d", wp, rp, nw, bl, ic);
      indication.started;
      // initialized
      wrPointer <= wp;
      rdPointer <= rp;
      numWords  <= nw;
      wrIterCnt <= ic;
      rdIterCnt <= ic;
      burstLen  <= bl;
   endmethod
   endinterface
   interface MemReadClient dmaReadClient = cons(re.dmaClient, nil);
   interface MemWriteClient dmaWriteClient = cons(we.dmaClient, nil);
endmodule


clean up

clean up

// Copyright (c) 2016 Cornell University

// 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 ClientServer::*;
import Connectable::*;
import DbgDefs::*;
import DefaultValue::*;
import Ethernet::*;
import FIFO::*;
import FIFOF::*;
import FShow::*;
import GetPut::*;
import List::*;
import MatchTable::*;
import MatchTableSim::*;
import PacketBuffer::*;
import Pipe::*;
import SpecialFIFOs::*;
import StmtFSM::*;
import Utils::*;
import Vector::*;

typedef struct {
  Bit#(1) hit;
  Bit#(1) p4_action;
  Bit#(9) action_1_arg0;
} RoutingRespT deriving (Bits, Eq);

instance DefaultValue#(RoutingRespT);
  defaultValue = unpack(0);
endinstance
instance DefaultMask#(RoutingRespT);
  defaultMask = unpack(maxBound);
endinstance

instance FShow#(RoutingRespT);
  function Fmt fshow(RoutingRespT p);
    return $format("RoutingRespT: hit=%h, p4_action=%h, action_1_arg0=%h", p.hit, p.p4_action, p.action_1_arg0);
  endfunction
endinstance

typedef struct {
   Maybe#(Bit#(16)) msgtype; // ethernet$msgtype
   Maybe#(Bit#(48)) dstAddr; // ethernet$dstAddr
   Maybe#(Bit#(16)) etherType; // ethernet$etherType
   Maybe#(Bit#(8))  protocol; // ipv4$protocol
   Maybe#(Bit#(16)) dstPort; // ipv4$dstPort
   Maybe#(Bool) valid_ethernet;
   Maybe#(Bool) valid_arp;
   Maybe#(Bool) valid_ipv4;
   Maybe#(Bool) valid_ipv6;
   Maybe#(Bool) valid_udp;
} MetadataT deriving (Bits, Eq);

instance DefaultValue#(MetadataT);
defaultValue =
MetadataT {
   msgtype: tagged Invalid,
   dstAddr: tagged Invalid,
   etherType: tagged Invalid,
   protocol: tagged Invalid,
   dstPort: tagged Invalid,
   valid_ethernet: tagged Invalid,
   valid_arp: tagged Invalid,
   valid_ipv4: tagged Invalid,
   valid_ipv6: tagged Invalid,
   valid_udp: tagged Invalid
};
endinstance

instance FShow#(MetadataT);
   function Fmt fshow(MetadataT p);
      return $format("msgtype=", fshow(p.msgtype), ",")+
             $format("dstAddr=", fshow(p.dstAddr), ",")+
             $format("etherType=", fshow(p.etherType), ",")+
             $format("protocol=", fshow(p.protocol), ",")+
             $format("dstPort=", fshow(p.dstPort), ",");
   endfunction
endinstance

typedef union tagged {
   struct {
      PacketInstance pkt;
   } PacketMemRequest;

   struct {
      PacketInstance pkt;
      MetadataT meta;
   } ForwardQueueRequest;

   struct {
      PacketInstance pkt;
      MetadataT meta;
   } DefaultRequest;
} MetadataRequest deriving (Bits, Eq, FShow);

typedef union tagged {
   struct {
      PacketInstance pkt;
      MetadataT meta;
   } DstMacResponse;
} MetadataResponse deriving (Bits, Eq, FShow);

typedef Client#(MetadataRequest, MetadataResponse) MetadataClient;
typedef Server#(MetadataRequest, MetadataResponse) MetadataServer;

function RoutingRespT extract_RoutingRespT(Bit#(11) data);
  Vector#(11, Bit#(1)) dataVec = unpack(data);
  Vector#(1, Bit#(1)) hit = takeAt(0, dataVec);
  Vector#(1, Bit#(1)) p4_action = takeAt(1, dataVec);
  Vector#(9, Bit#(1)) action_1_arg0 = takeAt(2, dataVec);
  RoutingRespT hdr = defaultValue;
  hdr.hit = pack(hit);
  hdr.p4_action = pack(p4_action);
  hdr.action_1_arg0 = pack(action_1_arg0);
  return hdr;
endfunction

typedef struct {
  Bit#(4) padding;
  Bit#(32) dstAddr;
} RoutingReqT deriving (Bits, Eq);

instance DefaultValue#(RoutingReqT);
  defaultValue = unpack(0);
endinstance
instance DefaultMask#(RoutingReqT);
  defaultMask = unpack(maxBound);
endinstance

instance FShow#(RoutingReqT);
  function Fmt fshow(RoutingReqT p);
    return $format("RoutingReqT: dstAddr=%h", p.dstAddr);
  endfunction
endinstance

function RoutingReqT extract_RoutingReqT(Bit#(32) data);
  Vector#(32, Bit#(1)) dataVec = unpack(data);
  Vector#(32, Bit#(1)) dstAddr = takeAt(0, dataVec);
  RoutingReqT hdr = defaultValue;
  hdr.dstAddr = pack(dstAddr);
  return hdr;
endfunction

typedef struct {
  Bit#(9) ingress_port;
  Bit#(32) packet_length;
  Bit#(9) egress_spec;
  Bit#(9) egress_port;
  Bit#(32) egress_instance;
  Bit#(32) instance_type;
  Bit#(32) clone_spec;
  Bit#(5) _padding;
} StandardMetadata deriving (Bits, Eq);

instance DefaultValue#(StandardMetadata);
  defaultValue = unpack(0);
endinstance
instance DefaultMask#(StandardMetadata);
  defaultMask = unpack(maxBound);
endinstance

instance FShow#(StandardMetadata);
  function Fmt fshow(StandardMetadata p);
    return $format("StandardMetadata: ingress_port=%h, packet_length=%h, egress_spec=%h, egress_port=%h, egress_instance=%h, instance_type=%h, clone_spec=%h, _padding=%h", p.ingress_port, p.packet_length, p.egress_spec, p.egress_port, p.egress_instance, p.instance_type, p.clone_spec, p._padding);
  endfunction
endinstance

function StandardMetadata extract_StandardMetadata(Bit#(160) data);
  Vector#(160, Bit#(1)) dataVec = unpack(data);
  Vector#(9, Bit#(1)) ingress_port = takeAt(0, dataVec);
  Vector#(32, Bit#(1)) packet_length = takeAt(9, dataVec);
  Vector#(9, Bit#(1)) egress_spec = takeAt(41, dataVec);
  Vector#(9, Bit#(1)) egress_port = takeAt(50, dataVec);
  Vector#(32, Bit#(1)) egress_instance = takeAt(59, dataVec);
  Vector#(32, Bit#(1)) instance_type = takeAt(91, dataVec);
  Vector#(32, Bit#(1)) clone_spec = takeAt(123, dataVec);
  Vector#(5, Bit#(1)) _padding = takeAt(155, dataVec);
  StandardMetadata hdr = defaultValue;
  hdr.ingress_port = pack(ingress_port);
  hdr.packet_length = pack(packet_length);
  hdr.egress_spec = pack(egress_spec);
  hdr.egress_port = pack(egress_port);
  hdr.egress_instance = pack(egress_instance);
  hdr.instance_type = pack(instance_type);
  hdr.clone_spec = pack(clone_spec);
  hdr._padding = pack(_padding);
  return hdr;
endfunction

typedef struct {
  Bit#(4) version;
  Bit#(4) ihl;
  Bit#(8) diffserv;
  Bit#(16) totalLen;
  Bit#(16) identification;
  Bit#(3) flags;
  Bit#(13) fragOffset;
  Bit#(8) ttl;
  Bit#(8) protocol;
  Bit#(16) hdrChecksum;
  Bit#(32) srcAddr;
  Bit#(32) dstAddr;
} Ipv4T deriving (Bits, Eq);

instance DefaultValue#(Ipv4T);
  defaultValue = unpack(0);
endinstance
instance DefaultMask#(Ipv4T);
  defaultMask = unpack(maxBound);
endinstance

instance FShow#(Ipv4T);
  function Fmt fshow(Ipv4T p);
    return $format("Ipv4T: version=%h, ihl=%h, diffserv=%h, totalLen=%h, identification=%h, flags=%h, fragOffset=%h, ttl=%h, protocol=%h, hdrChecksum=%h, srcAddr=%h, dstAddr=%h", p.version, p.ihl, p.diffserv, p.totalLen, p.identification, p.flags, p.fragOffset, p.ttl, p.protocol, p.hdrChecksum, p.srcAddr, p.dstAddr);
  endfunction
endinstance

function Ipv4T extract_Ipv4T(Bit#(160) data);
  Vector#(160, Bit#(1)) dataVec = unpack(data);
  Vector#(4, Bit#(1)) version = takeAt(0, dataVec);
  Vector#(4, Bit#(1)) ihl = takeAt(4, dataVec);
  Vector#(8, Bit#(1)) diffserv = takeAt(8, dataVec);
  Vector#(16, Bit#(1)) totalLen = takeAt(16, dataVec);
  Vector#(16, Bit#(1)) identification = takeAt(32, dataVec);
  Vector#(3, Bit#(1)) flags = takeAt(48, dataVec);
  Vector#(13, Bit#(1)) fragOffset = takeAt(51, dataVec);
  Vector#(8, Bit#(1)) ttl = takeAt(64, dataVec);
  Vector#(8, Bit#(1)) protocol = takeAt(72, dataVec);
  Vector#(16, Bit#(1)) hdrChecksum = takeAt(80, dataVec);
  Vector#(32, Bit#(1)) srcAddr = takeAt(96, dataVec);
  Vector#(32, Bit#(1)) dstAddr = takeAt(128, dataVec);
  Ipv4T hdr = defaultValue;
  hdr.version = pack(version);
  hdr.ihl = pack(ihl);
  hdr.diffserv = pack(diffserv);
  hdr.totalLen = pack(totalLen);
  hdr.identification = pack(identification);
  hdr.flags = pack(flags);
  hdr.fragOffset = pack(fragOffset);
  hdr.ttl = pack(ttl);
  hdr.protocol = pack(protocol);
  hdr.hdrChecksum = pack(hdrChecksum);
  hdr.srcAddr = pack(srcAddr);
  hdr.dstAddr = pack(dstAddr);
  return hdr;
endfunction

typedef struct {
  Bit#(48) dstAddr;
  Bit#(48) srcAddr;
  Bit#(16) etherType;
} EthernetT deriving (Bits, Eq);

instance DefaultValue#(EthernetT);
  defaultValue = unpack(0);
endinstance
instance DefaultMask#(EthernetT);
  defaultMask = unpack(maxBound);
endinstance

instance FShow#(EthernetT);
  function Fmt fshow(EthernetT p);
    return $format("EthernetT: dstAddr=%h, srcAddr=%h, etherType=%h", p.dstAddr, p.srcAddr, p.etherType);
  endfunction
endinstance

function EthernetT extract_EthernetT(Bit#(112) data);
  Vector#(112, Bit#(1)) dataVec = unpack(data);
  Vector#(48, Bit#(1)) dstAddr = takeAt(0, dataVec);
  Vector#(48, Bit#(1)) srcAddr = takeAt(48, dataVec);
  Vector#(16, Bit#(1)) etherType = takeAt(96, dataVec);
  EthernetT hdr = defaultValue;
  hdr.dstAddr = pack(dstAddr);
  hdr.srcAddr = pack(srcAddr);
  hdr.etherType = pack(etherType);
  return hdr;
endfunction

typedef struct {
  Bit#(32) dstAddr;
  Bit#(16) etherType;
} MetaT deriving (Bits, Eq);

instance DefaultValue#(MetaT);
  defaultValue = unpack(0);
endinstance
instance DefaultMask#(MetaT);
  defaultMask = unpack(maxBound);
endinstance

instance FShow#(MetaT);
  function Fmt fshow(MetaT p);
    return $format("MetaT: dstAddr=%h, etherType=%h", p.dstAddr, p.etherType);
  endfunction
endinstance

function MetaT extract_MetaT(Bit#(48) data);
  Vector#(48, Bit#(1)) dataVec = unpack(data);
  Vector#(32, Bit#(1)) dstAddr = takeAt(0, dataVec);
  Vector#(16, Bit#(1)) etherType = takeAt(32, dataVec);
  MetaT hdr = defaultValue;
  hdr.dstAddr = pack(dstAddr);
  hdr.etherType = pack(etherType);
  return hdr;
endfunction

interface Routing;
  interface Client#(MetadataRequest, MetadataResponse) next;
endinterface

module mkRouting#(Client#(MetadataRequest, MetadataResponse) md)(Routing);
  let verbose = True;

  FIFO#(MetadataRequest) outReqFifo <- mkFIFO;
  FIFO#(MetadataResponse) inRespFifo <- mkFIFO;

  MatchTable#(512, SizeOf#(RoutingReqT), SizeOf#(RoutingRespT)) matchTable <- mkMatchTable;

  rule handleRequest;
    let v <- md.request.get;
    case (v) matches
      default: begin

      end
    endcase
  endrule

  rule handleResponse;

  endrule

  interface next = (interface Client#(MetadataRequest, MetadataResponse);
    interface request = toGet(outReqFifo);
    interface response = toPut(inRespFifo);
  endinterface);
endmodule

interface BbForward;
endinterface

module mkBbForward(BbForward);

endmodule

interface BbNop;
endinterface

module mkBbNop(BbNop);

endmodule

interface Ingress0;
   interface PipeOut#(MetadataRequest) eventPktSend;
endinterface

module mkIngress0#(Vector#(numClients, MetadataClient) mdc)(Ingress0);
  let verbose = True;
  FIFOF#(MetadataRequest) currPacketFifo <- mkFIFOF;
  FIFO#(MetadataRequest) defaultReqFifo <- mkFIFO;
  FIFO#(MetadataResponse) defaultRespFifo <- mkFIFO;
  Vector#(numClients, MetadataServer) mds = newVector;
  for (Integer i=0; i<valueOf(numClients); i=i+1) begin
    mds[i] = (interface MetadataServer;
      interface Put request = toPut(defaultReqFifo);
      interface Get response = toGet(defaultRespFifo);
    endinterface);
  end
  mkConnection(mdc, mds);

  function MetadataClient toMetadataClient(FIFO#(MetadataRequest) reqFifo,
                                           FIFO#(MetadataResponse) respFifo);
    MetadataClient ret_ifc;
    ret_ifc = (interface MetadataClient;
      interface Get request = toGet(reqFifo);
      interface Put response = toPut(respFifo);
    endinterface);
    return ret_ifc;
  endfunction

  rule default_next_control_state if (defaultReqFifo.first matches tagged DefaultRequest {pkt: .pkt, meta: .meta});
    defaultReqFifo.deq;
    $display("(%0d) move default packet %x", $time, pkt, fshow(meta));
    MetadataRequest req = tagged ForwardQueueRequest {pkt: pkt, meta: meta};
    currPacketFifo.enq(req);
  endrule

  interface eventPktSend = toPipeOut(currPacketFifo);
endmodule

typedef enum {
   StateParseStart,
   StateParseEthernet,
   StateParseIpv4
} ParserState deriving (Bits, Eq);
module mkStateParseStart#(Reg#(ParserState) state, FIFOF#(EtherData) datain, Wire#(Bool) start_fsm)(Empty);
  rule load_packet if (state == StateParseStart);
    let v = datain.first;
    if (v.sop) begin
      state <= StateParseEthernet;
      start_fsm <= True;
    end
    else begin
      datain.deq;
      start_fsm <= False;
    end
  endrule
endmodule

interface ParseEthernet;

  interface Get#(Bit#(16)) parse_ipv4;
  interface Get#(Bit#(16)) parsedOut_ethernet_etherType;
  method Action start;
  method Action stop;
endinterface
module mkStateParseEthernet#(Reg#(ParserState) state, FIFOF#(EtherData) datain)(ParseEthernet);
  let verbose = True;
  FIFOF#(Bit#(16)) unparsed_parse_ipv4_fifo <- mkSizedFIFOF(1);

  FIFOF#(Bit#(16)) parsed_etherType_fifo <- mkFIFOF;

  Wire#(Bit#(128)) packet_in_wire <- mkDWire(0);
  Vector#(4, Wire#(Maybe#(ParserState))) next_state_wire <- replicateM(mkDWire(tagged Invalid));
  PulseWire start_wire <- mkPulseWire();
  PulseWire clear_wire <- mkPulseWire();
  (* fire_when_enabled *)
  rule arbitrate_outgoing_state if (state == StateParseEthernet);
    Vector#(4, Bool) next_state_valid = replicate(False);
    Bool stateSet = False;
    for (Integer port=0; port<4; port=port+1) begin
      next_state_valid[port] = isValid(next_state_wire[port]);
      if (!stateSet && next_state_valid[port]) begin
        stateSet = True;
        ParserState next_state = fromMaybe(?, next_state_wire[port]);
        state <= next_state;
      end
    end
  endrule

  function ParserState compute_next_state(Bit#(16) v);
    ParserState nextState = StateParseStart;
    case (byteSwap(v)) matches
      'h800: begin
        nextState = StateParseIpv4;
      end
      default: begin
        nextState = StateParseStart;
      end
    endcase
    return nextState;
  endfunction

  rule load_packet if (state == StateParseEthernet);
    let data_current <- toGet(datain).get;
    packet_in_wire <= data_current.data;
  endrule

  Stmt stmt_parse_ethernet =
  seq
  action
    let data_this_cycle = packet_in_wire;
    Vector#(128, Bit#(1)) dataVec = unpack(data_this_cycle);
    let hdr = extract_EthernetT(pack(takeAt(0, dataVec)));
    $display(fshow(hdr));
    Vector#(16, Bit#(1)) unparsed = takeAt(0, dataVec);
    let nextState = compute_next_state(hdr.etherType);
    if (verbose) $display("Goto state ", nextState);
    if (nextState == StateParseIpv4) begin
      unparsed_parse_ipv4_fifo.enq(pack(unparsed));
    end
    parsed_etherType_fifo.enq(hdr.etherType);
    next_state_wire[0] <= tagged Valid nextState;
  endaction
  endseq;
  FSM fsm_parse_ethernet <- mkFSM(stmt_parse_ethernet);
  rule start_fsm if (start_wire);
    fsm_parse_ethernet.start;
  endrule
  rule clear_fsm if (clear_wire);
    fsm_parse_ethernet.abort;
  endrule

  method start = start_wire.send;
  method stop = clear_wire.send;
  interface parse_ipv4 = toGet(unparsed_parse_ipv4_fifo);
  interface parsedOut_ethernet_etherType = toGet(parsed_etherType_fifo);
endmodule

interface ParseIpv4;
  interface Put#(Bit#(16)) parse_ethernet;
  interface Get#(Bit#(8)) parsedOut_ipv4_protocol;
  method Action start;
  method Action stop;
endinterface
module mkStateParseIpv4#(Reg#(ParserState) state, FIFOF#(EtherData) datain, FIFOF#(ParserState) parseStateFifo)(ParseIpv4);
  let verbose = False;
  FIFOF#(Bit#(16)) unparsed_parse_ethernet_fifo <- mkBypassFIFOF;

  FIFOF#(Bit#(8)) parsed_ipv4_protocol_fifo <- mkFIFOF;
  FIFOF#(Bit#(144)) internal_fifo_144 <- mkSizedFIFOF(1);

  Wire#(Bit#(128)) packet_in_wire <- mkDWire(0);
  Vector#(4, Wire#(Maybe#(ParserState))) next_state_wire <- replicateM(mkDWire(tagged Invalid));
  PulseWire start_wire <- mkPulseWire();
  PulseWire clear_wire <- mkPulseWire();
  (* fire_when_enabled *)
  rule arbitrate_outgoing_state if (state == StateParseIpv4);
    Vector#(4, Bool) next_state_valid = replicate(False);
    Bool stateSet = False;
    for (Integer port=0; port<4; port=port+1) begin
      next_state_valid[port] = isValid(next_state_wire[port]);
      if (!stateSet && next_state_valid[port]) begin
        stateSet = True;
        ParserState next_state = fromMaybe(?, next_state_wire[port]);
        state <= next_state;
      end
    end
  endrule

  rule load_packet if (state == StateParseIpv4);
    let data_current <- toGet(datain).get;
    packet_in_wire <= data_current.data;
  endrule

  Stmt stmt_parse_ipv4 =
  seq
  action
    let data_this_cycle = packet_in_wire;
    let data_last_cycle <- toGet(unparsed_parse_ethernet_fifo).get;
    Bit#(144) data = {data_this_cycle, data_last_cycle};
    internal_fifo_144.enq(data);
  endaction
  action
    let data_this_cycle = packet_in_wire;
    let data_last_cycle <- toGet(internal_fifo_144).get;
    Bit#(272) data = {data_this_cycle, data_last_cycle};
    Vector#(272, Bit#(1)) dataVec = unpack(data);
    let hdr = extract_Ipv4T(pack(takeAt(0, dataVec)));
    $display(fshow(hdr));
    parseStateFifo.enq(StateParseIpv4);
    parsed_ipv4_protocol_fifo.enq(hdr.protocol);
    next_state_wire[0] <= tagged Valid StateParseStart;
  endaction
  endseq;
  FSM fsm_parse_ipv4 <- mkFSM(stmt_parse_ipv4);
  rule start_fsm if (start_wire);
    fsm_parse_ipv4.start;
  endrule
  rule clear_fsm if (clear_wire);
    fsm_parse_ipv4.abort;
  endrule

  method start = start_wire.send;
  method stop = clear_wire.send;
  interface parse_ethernet = toPut(unparsed_parse_ethernet_fifo);
  interface parsedOut_ipv4_protocol = toGet(parsed_ipv4_protocol_fifo);
endmodule

interface Parser;
  interface Put#(EtherData) frameIn;
  interface Get#(MetadataT) meta;
  method ParserPerfRec read_perf_info;
endinterface
typedef 4 PortMax;
(* synthesize *)
module mkParser(Parser);
  Reg#(ParserState) curr_state <- mkReg(StateParseStart);
  Reg#(Bool) started <- mkReg(False);
  FIFOF#(EtherData) data_in_fifo <- mkFIFOF;
  Wire#(Bool) start_fsm <- mkDWire(False);

  Vector#(PortMax, FIFOF#(ParserState)) parse_state_in_fifo <- replicateM(mkGFIFOF(False, True)); // ungarded deq
  FIFOF#(ParserState) parse_state_out_fifo <- mkFIFOF;
  FIFOF#(MetadataT) metadata_out_fifo <- mkFIFOF;

  (* fire_when_enabled *)
  rule arbitrate_parse_state;
    Bool sentOne = False;
    for (Integer port=0; port<valueOf(PortMax); port=port+1) begin
      if (!sentOne && parse_state_in_fifo[port].notEmpty()) begin
        ParserState state <- toGet(parse_state_in_fifo[port]).get;
        sentOne = True;
        parse_state_out_fifo.enq(state);
      end
    end
  endrule

  Empty init_state <- mkStateParseStart(curr_state, data_in_fifo, start_fsm);
  ParseIpv4 parse_ipv4 <- mkStateParseIpv4(curr_state, data_in_fifo, parse_state_in_fifo[0]);
  ParseEthernet parse_ethernet <- mkStateParseEthernet(curr_state, data_in_fifo);
  mkConnection(parse_ipv4.parse_ethernet, parse_ethernet.parse_ipv4);
  rule start if (start_fsm);
    if (!started) begin
      parse_ipv4.start;
      parse_ethernet.start;
      started <= True;
    end
  endrule

  rule clear if (!start_fsm && curr_state == StateParseStart);
    if (started) begin
      parse_ipv4.stop;
      parse_ethernet.stop;
      started <= False;
    end
  endrule

  // handle ipv4 packet
  rule handle_ipv4_packet if (parse_state_out_fifo.first == StateParseIpv4);
    parse_state_out_fifo.deq;
    let protocol <- toGet(parse_ipv4.parsedOut_ipv4_protocol).get;
    let etherType <- toGet(parse_ethernet.parsedOut_ethernet_etherType).get;
    $display("(%0d) handle ipv4", $time);
    MetadataT meta = defaultValue;
    meta.protocol = tagged Valid protocol;
    meta.etherType = tagged Valid etherType;
    meta.valid_ipv4 = tagged Valid True;
    meta.valid_ethernet = tagged Valid True;
    metadata_out_fifo.enq(meta);
  endrule

  interface frameIn = toPut(data_in_fifo);
  interface meta = toGet(metadata_out_fifo);
endmodule

typedef enum {
  StateDeparseIdle,
  StateDeparseEthernet,
  StateDeparseIpv4
} DeparserState deriving (Bits, Eq, FShow);

function Tuple2#(EthernetT, EthernetT) toEthernet(MetadataT meta);
   EthernetT data = defaultValue;
   EthernetT mask = defaultMask;
   data.dstAddr = fromMaybe(?, meta.dstAddr);
   mask.dstAddr = 0;
   data.etherType = fromMaybe(?, meta.etherType);
   mask.etherType = 0;
   return tuple2(data, mask);
endfunction

function Tuple2#(Ipv4T, Ipv4T) toIpv4(MetadataT meta);
   Ipv4T ipv4 = defaultValue;
   Ipv4T mask = defaultMask;
   ipv4.protocol = fromMaybe(?, meta.protocol);
   mask.protocol = 0;
   return tuple2(ipv4, mask);
endfunction

module mkStateDeparseIdle#(Reg#(DeparserState) state, FIFOF#(EtherData) datain, FIFOF#(EtherData) dataout, Wire#(Bool) start_fsm)(Empty);

   rule load_packet if (state == StateDeparseIdle);
      let v = datain.first;
      if (v.sop) begin
         state <= StateDeparseEthernet;
         start_fsm <= True;
         $display("(%0d) Deparse Ethernet Start", $time);
      end
      else begin
         datain.deq;
         dataout.enq(v);
         $display("(%0d) payload ", $time, fshow(v));
         start_fsm <= False;
      end
   endrule
endmodule

interface DeparseEthernet;
  interface Get#(EtherData) deparse_ipv4;
  method Action start;
  method Action clear;
endinterface
module mkStateDeparseEthernet#(Reg#(DeparserState) state, FIFOF#(EtherData) datain, FIFOF#(EtherData) dataout, FIFOF#(EthernetT) meta_fifo, FIFOF#(EthernetT) mask_fifo)(DeparseEthernet);
  let verbose = False;
  Wire#(EtherData) packet_in_wire <- mkDWire(defaultValue);

  FIFO#(EtherData) deparse_ipv4_fifo <- mkFIFO;
  PulseWire start_wire <- mkPulseWire;
  PulseWire clear_wire <- mkPulseWire;

  function DeparserState compute_next_state(Bit#(16) v);
    DeparserState nextState = StateDeparseIdle;
    case (byteSwap(v)) matches
      'h800: begin
        nextState = StateDeparseIpv4;
      end
      default: begin
        nextState = StateDeparseIdle;
      end
    endcase
    return nextState;
  endfunction

  rule load_packet if (state == StateDeparseEthernet);
    let data_current <- toGet(datain).get;
    packet_in_wire <= data_current;
  endrule
  Stmt deparse_ethernet =
  seq
  action
    let data_this_cycle = packet_in_wire;
    Vector#(16, Bit#(1)) unused = takeAt(112, unpack(data_this_cycle.data));
    Vector#(112, Bit#(1)) data = takeAt(0, unpack(data_this_cycle.data));
    Vector#(112, Bit#(1)) curr_meta = takeAt(0, unpack(byteSwap(pack(meta_fifo.first))));
    Vector#(112, Bit#(1)) curr_mask = takeAt(0, unpack(byteSwap(pack(mask_fifo.first))));
    let masked_data = pack(data) & pack(curr_mask);
    let curr_data = masked_data | pack(curr_meta);
    EthernetT ethernet_t = unpack(pack(masked_data));
    data_this_cycle.data = {pack(unused), pack(curr_data)};
    let nextState = compute_next_state(ethernet_t.etherType);
    state <= nextState;
    if (nextState == StateDeparseIpv4) begin
      deparse_ipv4_fifo.enq(data_this_cycle);
    end
    meta_fifo.deq;
    mask_fifo.deq;
  endaction
  endseq;

  FSM fsm_deparse_ethernet <- mkFSM(deparse_ethernet);
  rule start_fsm if (start_wire);
    fsm_deparse_ethernet.start;
  endrule
  rule clear_fsm if (clear_wire);
    fsm_deparse_ethernet.abort;
  endrule
  method start = start_wire.send;
  method clear = clear_wire.send;
  interface deparse_ipv4 = toGet(deparse_ipv4_fifo);
endmodule

interface DeparseIpv4;
  interface Put#(EtherData) deparse_ethernet;
  method Action start;
  method Action clear;
endinterface
module mkStateDeparseIpv4#(Reg#(DeparserState) state, FIFOF#(EtherData) datain, FIFOF#(EtherData) dataout, FIFOF#(Ipv4T) meta_fifo, FIFOF#(Ipv4T) mask_fifo)(DeparseIpv4);
  let verbose = False;
  Wire#(EtherData) packet_in_wire <- mkDWire(defaultValue);
  FIFOF#(EtherData) deparse_ethernet_fifo <- mkBypassFIFOF;
  PulseWire start_wire <- mkPulseWire;
  PulseWire clear_wire <- mkPulseWire;

  rule load_packet if (state == StateDeparseIpv4 && !deparse_ethernet_fifo.notEmpty());
    let data_current <- toGet(datain).get;
    packet_in_wire <= data_current;
    $display("(%0d) IPv4: ", $time, fshow(data_current));
  endrule
  Stmt deparse_ipv4 =
  seq
  action
    let data_this_cycle <- toGet(deparse_ethernet_fifo).get;
    Vector#(112, Bit#(1)) unused = takeAt(0, unpack(data_this_cycle.data));
    Vector#(16, Bit#(1)) data = takeAt(112, unpack(data_this_cycle.data));
    Vector#(16, Bit#(1)) curr_meta = takeAt(0, unpack(byteSwap(pack(meta_fifo.first))));
    Vector#(16, Bit#(1)) curr_mask = takeAt(0, unpack(byteSwap(pack(mask_fifo.first))));
    let masked_data = pack(data) & pack(curr_mask);
    let curr_data = masked_data | pack(curr_meta);
    $display("(%0d) IPv4: [1] ", $time, fshow(data_this_cycle), " meta=%h, mask=%h", curr_mask, curr_meta);
    data_this_cycle.data = {pack(curr_data), pack(unused)};
    dataout.enq(data_this_cycle);
  endaction
  action
    let data_this_cycle = packet_in_wire;
    Vector#(128, Bit#(1)) data = takeAt(0, unpack(data_this_cycle.data));
    Vector#(128, Bit#(1)) curr_meta = takeAt(16, unpack(byteSwap(pack(meta_fifo.first))));
    Vector#(128, Bit#(1)) curr_mask = takeAt(16, unpack(byteSwap(pack(mask_fifo.first))));
    let masked_data = pack(data) & pack(curr_mask);
    let curr_data = masked_data | pack(curr_meta);
    data_this_cycle.data = {pack(curr_data)};
    dataout.enq(data_this_cycle);
    $display("(%0d) IPv4: [2] ", $time, fshow(data_this_cycle));
  endaction
  action
    let data_this_cycle = packet_in_wire;
    Vector#(112, Bit#(1)) unused = takeAt(16, unpack(data_this_cycle.data));
    Vector#(16, Bit#(1)) data = takeAt(0, unpack(data_this_cycle.data));
    Vector#(16, Bit#(1)) curr_meta = takeAt(144, unpack(byteSwap(pack(meta_fifo.first))));
    Vector#(16, Bit#(1)) curr_mask = takeAt(144, unpack(byteSwap(pack(mask_fifo.first))));
    let masked_data = pack(data) & pack(curr_mask);
    let curr_data = masked_data | pack(curr_meta);
    Ipv4T ipv4_t = unpack(pack(masked_data));
    data_this_cycle.data = {pack(unused), pack(curr_data)};
    $display("(%0d) compute_next_state protocol", $time, fshow(meta_fifo.first.protocol));
    $display("(%0d) Goto ", $time, fshow(nextState));
    dataout.enq(data_this_cycle);
    state <= StateDeparseIdle;
    meta_fifo.deq;
    mask_fifo.deq;
  endaction
  endseq;

  FSM fsm_deparse_ipv4 <- mkFSM(deparse_ipv4);
  rule start_fsm if (start_wire);
    fsm_deparse_ipv4.start;
  endrule
  rule clear_fsm if (clear_wire);
    fsm_deparse_ipv4.abort;
  endrule
  method start = start_wire.send;
  method clear = clear_wire.send;
  interface deparse_ethernet = toPut(deparse_ethernet_fifo);
endmodule

interface Deparser;
  interface PipeIn#(MetadataT) metadata;
  interface PktWriteServer writeServer;
  interface PktWriteClient writeClient;
  method DeparserPerfRec read_perf_info;
endinterface
(* synthesize *)
module mkDeparser(Deparser);
  let verbose = False;
  FIFOF#(EtherData) data_in_fifo <- mkSizedFIFOF(4);
  FIFOF#(EtherData) data_out_fifo <- mkFIFOF;
  FIFOF#(MetadataT) metadata_in_fifo <- mkFIFOF;
  Reg#(Bool) started <- mkReg(False);
  Wire#(Bool) start_fsm <- mkDWire(False);
  Reg#(DeparserState) curr_state <- mkReg(StateDeparseIdle);

  Vector#(PortMax, FIFOF#(DeparserState)) deparse_state_in_fifo <- replicateM(mkGFIFOF(False, True));
  FIFOF#(DeparserState) deparse_state_out_fifo <- mkFIFOF;
  FIFOF#(EthernetT) deparse_ethernet_meta_fifo <- mkFIFOF;
  FIFOF#(Ipv4T) deparse_ipv4_meta_fifo <- mkFIFOF;
  FIFOF#(EthernetT) deparse_ethernet_mask_fifo <- mkFIFOF;
  FIFOF#(Ipv4T) deparse_ipv4_mask_fifo <- mkFIFOF;

  Reg#(Bit#(32)) clk_cnt <- mkReg(0);
  Reg#(Bit#(32)) deparser_start_time <- mkReg(0);
  Reg#(Bit#(32)) deparser_end_time <- mkReg(0);
  rule clockrule;
    clk_cnt <= clk_cnt + 1;
  endrule

  (* fire_when_enabled *)
  rule arbitrate_deparse_state;
    Bool sentOne = False;
    for (Integer port = 0; port < valueOf(PortMax); port = port+1) begin
      if (!sentOne && deparse_state_in_fifo[port].notEmpty()) begin
        DeparserState state <- toGet(deparse_state_in_fifo[port]).get();
        sentOne = True;
        $display("(%0d) xxx arbitrate %h", $time, port);
        deparse_state_out_fifo.enq(state);
      end
    end
  endrule

  rule get_metadata;
     let v <- toGet(metadata_in_fifo).get;
     let ethernet = toEthernet(v);
     match {.data, .mask} = ethernet;
     deparse_ethernet_meta_fifo.enq(data);
     deparse_ethernet_mask_fifo.enq(mask);

     let ipv4 = toIpv4(v);
     match {.ipv4_data, .ipv4_mask} = ipv4;
     if (verbose) $display("(%0d) ipv4 meta", $time, fshow(ipv4));
     deparse_ipv4_meta_fifo.enq(ipv4_data);
     deparse_ipv4_mask_fifo.enq(ipv4_mask);
  endrule

  Empty init_state <- mkStateDeparseIdle(curr_state, data_in_fifo, data_out_fifo, start_fsm);
  DeparseEthernet deparse_ethernet <- mkStateDeparseEthernet(curr_state, data_in_fifo, data_out_fifo, deparse_ethernet_meta_fifo, deparse_ethernet_mask_fifo);
  DeparseIpv4 deparse_ipv4 <- mkStateDeparseIpv4(curr_state, data_in_fifo, data_out_fifo, deparse_ipv4_meta_fifo, deparse_ipv4_mask_fifo);
  mkConnection(deparse_ethernet.deparse_ipv4,deparse_ipv4.deparse_ethernet);

  rule start if (start_fsm);
    if (!started) begin
      deparse_ethernet.start;
      deparse_ipv4.start;
      started <= True;
      deparser_start_time <= clk_cnt;
    end
  endrule
  rule clear if (!start_fsm && curr_state == StateDeparseIdle);
    if (started) begin
      deparse_ethernet.clear;
      deparse_ipv4.clear;
      started <= False;
      deparser_end_time <= clk_cnt;
    end
  endrule
  interface PktWriteServer writeServer;
    interface writeData = toPut(data_in_fifo);
  endinterface
  interface PktWriteClient writeClient;
    interface writeData = toGet(data_out_fifo);
  endinterface
  interface metadata = toPipeIn(metadata_in_fifo);
  method DeparserPerfRec read_perf_info;
    return DeparserPerfRec {
      deparser_start_time: deparser_start_time,
      deparser_end_time: deparser_end_time
    };
  endmethod
endmodule

// Copyright (c) 2016 Cornell University

// 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 ClientServer::*;
import Connectable::*;
import DbgDefs::*;
import DefaultValue::*;
import Ethernet::*;
import FIFO::*;
import FIFOF::*;
import FShow::*;
import GetPut::*;
import List::*;
import MatchTable::*;
import MatchTableSim::*;
import PacketBuffer::*;
import Pipe::*;
import SpecialFIFOs::*;
import StmtFSM::*;
import Utils::*;
import Vector::*;

typedef struct {
  Bit#(1) hit;
  Bit#(1) p4_action;
  Bit#(9) action_1_arg0;
} RoutingRespT deriving (Bits, Eq);

instance DefaultValue#(RoutingRespT);
  defaultValue = unpack(0);
endinstance
instance DefaultMask#(RoutingRespT);
  defaultMask = unpack(maxBound);
endinstance

instance FShow#(RoutingRespT);
  function Fmt fshow(RoutingRespT p);
    return $format("RoutingRespT: hit=%h, p4_action=%h, action_1_arg0=%h", p.hit, p.p4_action, p.action_1_arg0);
  endfunction
endinstance

typedef struct {
   Maybe#(Bit#(16)) msgtype; // ethernet$msgtype
   Maybe#(Bit#(48)) dstAddr; // ethernet$dstAddr
   Maybe#(Bit#(16)) etherType; // ethernet$etherType
   Maybe#(Bit#(8))  protocol; // ipv4$protocol
   Maybe#(Bit#(16)) dstPort; // ipv4$dstPort
   Maybe#(Bool) valid_ethernet;
   Maybe#(Bool) valid_arp;
   Maybe#(Bool) valid_ipv4;
   Maybe#(Bool) valid_ipv6;
   Maybe#(Bool) valid_udp;
} MetadataT deriving (Bits, Eq);

instance DefaultValue#(MetadataT);
defaultValue =
MetadataT {
   msgtype: tagged Invalid,
   dstAddr: tagged Invalid,
   etherType: tagged Invalid,
   protocol: tagged Invalid,
   dstPort: tagged Invalid,
   valid_ethernet: tagged Invalid,
   valid_arp: tagged Invalid,
   valid_ipv4: tagged Invalid,
   valid_ipv6: tagged Invalid,
   valid_udp: tagged Invalid
};
endinstance

instance FShow#(MetadataT);
   function Fmt fshow(MetadataT p);
      return $format("msgtype=", fshow(p.msgtype), ",")+
             $format("dstAddr=", fshow(p.dstAddr), ",")+
             $format("etherType=", fshow(p.etherType), ",")+
             $format("protocol=", fshow(p.protocol), ",")+
             $format("dstPort=", fshow(p.dstPort), ",");
   endfunction
endinstance

typedef union tagged {
   struct {
      PacketInstance pkt;
   } PacketMemRequest;

   struct {
      PacketInstance pkt;
      MetadataT meta;
   } ForwardQueueRequest;

   struct {
      PacketInstance pkt;
      MetadataT meta;
   } DefaultRequest;
} MetadataRequest deriving (Bits, Eq, FShow);

typedef union tagged {
   struct {
      PacketInstance pkt;
      MetadataT meta;
   } DstMacResponse;
} MetadataResponse deriving (Bits, Eq, FShow);

typedef Client#(MetadataRequest, MetadataResponse) MetadataClient;
typedef Server#(MetadataRequest, MetadataResponse) MetadataServer;

function RoutingRespT extract_RoutingRespT(Bit#(11) data);
  Vector#(11, Bit#(1)) dataVec = unpack(data);
  Vector#(1, Bit#(1)) hit = takeAt(0, dataVec);
  Vector#(1, Bit#(1)) p4_action = takeAt(1, dataVec);
  Vector#(9, Bit#(1)) action_1_arg0 = takeAt(2, dataVec);
  RoutingRespT hdr = defaultValue;
  hdr.hit = pack(hit);
  hdr.p4_action = pack(p4_action);
  hdr.action_1_arg0 = pack(action_1_arg0);
  return hdr;
endfunction

typedef struct {
  Bit#(4) padding;
  Bit#(32) dstAddr;
} RoutingReqT deriving (Bits, Eq);

instance DefaultValue#(RoutingReqT);
  defaultValue = unpack(0);
endinstance
instance DefaultMask#(RoutingReqT);
  defaultMask = unpack(maxBound);
endinstance

instance FShow#(RoutingReqT);
  function Fmt fshow(RoutingReqT p);
    return $format("RoutingReqT: dstAddr=%h", p.dstAddr);
  endfunction
endinstance

function RoutingReqT extract_RoutingReqT(Bit#(32) data);
  Vector#(32, Bit#(1)) dataVec = unpack(data);
  Vector#(32, Bit#(1)) dstAddr = takeAt(0, dataVec);
  RoutingReqT hdr = defaultValue;
  hdr.dstAddr = pack(dstAddr);
  return hdr;
endfunction

typedef struct {
  Bit#(9) ingress_port;
  Bit#(32) packet_length;
  Bit#(9) egress_spec;
  Bit#(9) egress_port;
  Bit#(32) egress_instance;
  Bit#(32) instance_type;
  Bit#(32) clone_spec;
  Bit#(5) _padding;
} StandardMetadata deriving (Bits, Eq);

instance DefaultValue#(StandardMetadata);
  defaultValue = unpack(0);
endinstance
instance DefaultMask#(StandardMetadata);
  defaultMask = unpack(maxBound);
endinstance

instance FShow#(StandardMetadata);
  function Fmt fshow(StandardMetadata p);
    return $format("StandardMetadata: ingress_port=%h, packet_length=%h, egress_spec=%h, egress_port=%h, egress_instance=%h, instance_type=%h, clone_spec=%h, _padding=%h", p.ingress_port, p.packet_length, p.egress_spec, p.egress_port, p.egress_instance, p.instance_type, p.clone_spec, p._padding);
  endfunction
endinstance

function StandardMetadata extract_StandardMetadata(Bit#(160) data);
  Vector#(160, Bit#(1)) dataVec = unpack(data);
  Vector#(9, Bit#(1)) ingress_port = takeAt(0, dataVec);
  Vector#(32, Bit#(1)) packet_length = takeAt(9, dataVec);
  Vector#(9, Bit#(1)) egress_spec = takeAt(41, dataVec);
  Vector#(9, Bit#(1)) egress_port = takeAt(50, dataVec);
  Vector#(32, Bit#(1)) egress_instance = takeAt(59, dataVec);
  Vector#(32, Bit#(1)) instance_type = takeAt(91, dataVec);
  Vector#(32, Bit#(1)) clone_spec = takeAt(123, dataVec);
  Vector#(5, Bit#(1)) _padding = takeAt(155, dataVec);
  StandardMetadata hdr = defaultValue;
  hdr.ingress_port = pack(ingress_port);
  hdr.packet_length = pack(packet_length);
  hdr.egress_spec = pack(egress_spec);
  hdr.egress_port = pack(egress_port);
  hdr.egress_instance = pack(egress_instance);
  hdr.instance_type = pack(instance_type);
  hdr.clone_spec = pack(clone_spec);
  hdr._padding = pack(_padding);
  return hdr;
endfunction

typedef struct {
  Bit#(4) version;
  Bit#(4) ihl;
  Bit#(8) diffserv;
  Bit#(16) totalLen;
  Bit#(16) identification;
  Bit#(3) flags;
  Bit#(13) fragOffset;
  Bit#(8) ttl;
  Bit#(8) protocol;
  Bit#(16) hdrChecksum;
  Bit#(32) srcAddr;
  Bit#(32) dstAddr;
} Ipv4T deriving (Bits, Eq);

instance DefaultValue#(Ipv4T);
  defaultValue = unpack(0);
endinstance
instance DefaultMask#(Ipv4T);
  defaultMask = unpack(maxBound);
endinstance

instance FShow#(Ipv4T);
  function Fmt fshow(Ipv4T p);
    return $format("Ipv4T: version=%h, ihl=%h, diffserv=%h, totalLen=%h, identification=%h, flags=%h, fragOffset=%h, ttl=%h, protocol=%h, hdrChecksum=%h, srcAddr=%h, dstAddr=%h", p.version, p.ihl, p.diffserv, p.totalLen, p.identification, p.flags, p.fragOffset, p.ttl, p.protocol, p.hdrChecksum, p.srcAddr, p.dstAddr);
  endfunction
endinstance

function Ipv4T extract_Ipv4T(Bit#(160) data);
  Vector#(160, Bit#(1)) dataVec = unpack(data);
  Vector#(4, Bit#(1)) version = takeAt(0, dataVec);
  Vector#(4, Bit#(1)) ihl = takeAt(4, dataVec);
  Vector#(8, Bit#(1)) diffserv = takeAt(8, dataVec);
  Vector#(16, Bit#(1)) totalLen = takeAt(16, dataVec);
  Vector#(16, Bit#(1)) identification = takeAt(32, dataVec);
  Vector#(3, Bit#(1)) flags = takeAt(48, dataVec);
  Vector#(13, Bit#(1)) fragOffset = takeAt(51, dataVec);
  Vector#(8, Bit#(1)) ttl = takeAt(64, dataVec);
  Vector#(8, Bit#(1)) protocol = takeAt(72, dataVec);
  Vector#(16, Bit#(1)) hdrChecksum = takeAt(80, dataVec);
  Vector#(32, Bit#(1)) srcAddr = takeAt(96, dataVec);
  Vector#(32, Bit#(1)) dstAddr = takeAt(128, dataVec);
  Ipv4T hdr = defaultValue;
  hdr.version = pack(version);
  hdr.ihl = pack(ihl);
  hdr.diffserv = pack(diffserv);
  hdr.totalLen = pack(totalLen);
  hdr.identification = pack(identification);
  hdr.flags = pack(flags);
  hdr.fragOffset = pack(fragOffset);
  hdr.ttl = pack(ttl);
  hdr.protocol = pack(protocol);
  hdr.hdrChecksum = pack(hdrChecksum);
  hdr.srcAddr = pack(srcAddr);
  hdr.dstAddr = pack(dstAddr);
  return hdr;
endfunction

typedef struct {
  Bit#(48) dstAddr;
  Bit#(48) srcAddr;
  Bit#(16) etherType;
} EthernetT deriving (Bits, Eq);

instance DefaultValue#(EthernetT);
  defaultValue = unpack(0);
endinstance
instance DefaultMask#(EthernetT);
  defaultMask = unpack(maxBound);
endinstance

instance FShow#(EthernetT);
  function Fmt fshow(EthernetT p);
    return $format("EthernetT: dstAddr=%h, srcAddr=%h, etherType=%h", p.dstAddr, p.srcAddr, p.etherType);
  endfunction
endinstance

function EthernetT extract_EthernetT(Bit#(112) data);
  Vector#(112, Bit#(1)) dataVec = unpack(data);
  Vector#(48, Bit#(1)) dstAddr = takeAt(0, dataVec);
  Vector#(48, Bit#(1)) srcAddr = takeAt(48, dataVec);
  Vector#(16, Bit#(1)) etherType = takeAt(96, dataVec);
  EthernetT hdr = defaultValue;
  hdr.dstAddr = pack(dstAddr);
  hdr.srcAddr = pack(srcAddr);
  hdr.etherType = pack(etherType);
  return hdr;
endfunction

typedef struct {
  Bit#(32) dstAddr;
  Bit#(16) etherType;
} MetaT deriving (Bits, Eq);

instance DefaultValue#(MetaT);
  defaultValue = unpack(0);
endinstance
instance DefaultMask#(MetaT);
  defaultMask = unpack(maxBound);
endinstance

instance FShow#(MetaT);
  function Fmt fshow(MetaT p);
    return $format("MetaT: dstAddr=%h, etherType=%h", p.dstAddr, p.etherType);
  endfunction
endinstance

function MetaT extract_MetaT(Bit#(48) data);
  Vector#(48, Bit#(1)) dataVec = unpack(data);
  Vector#(32, Bit#(1)) dstAddr = takeAt(0, dataVec);
  Vector#(16, Bit#(1)) etherType = takeAt(32, dataVec);
  MetaT hdr = defaultValue;
  hdr.dstAddr = pack(dstAddr);
  hdr.etherType = pack(etherType);
  return hdr;
endfunction

interface Routing;
  interface Client#(MetadataRequest, MetadataResponse) next;
endinterface

module mkRouting#(Client#(MetadataRequest, MetadataResponse) md)(Routing);
  let verbose = True;

  FIFO#(MetadataRequest) outReqFifo <- mkFIFO;
  FIFO#(MetadataResponse) inRespFifo <- mkFIFO;

  MatchTable#(512, SizeOf#(RoutingReqT), SizeOf#(RoutingRespT)) matchTable <- mkMatchTable;

  rule handleRequest;
    let v <- md.request.get;
    case (v) matches
      default: begin

      end
    endcase
  endrule

  rule handleResponse;

  endrule

  interface next = (interface Client#(MetadataRequest, MetadataResponse);
    interface request = toGet(outReqFifo);
    interface response = toPut(inRespFifo);
  endinterface);
endmodule

interface BbForward;
endinterface

module mkBbForward(BbForward);

endmodule

interface BbNop;
endinterface

module mkBbNop(BbNop);

endmodule

interface Ingress0;
   interface PipeOut#(MetadataRequest) eventPktSend;
endinterface

module mkIngress0#(Vector#(numClients, MetadataClient) mdc)(Ingress0);
  let verbose = True;
  FIFOF#(MetadataRequest) currPacketFifo <- mkFIFOF;
  FIFO#(MetadataRequest) defaultReqFifo <- mkFIFO;
  FIFO#(MetadataResponse) defaultRespFifo <- mkFIFO;
  Vector#(numClients, MetadataServer) mds = newVector;
  for (Integer i=0; i<valueOf(numClients); i=i+1) begin
    mds[i] = (interface MetadataServer;
      interface Put request = toPut(defaultReqFifo);
      interface Get response = toGet(defaultRespFifo);
    endinterface);
  end
  mkConnection(mdc, mds);

  function MetadataClient toMetadataClient(FIFO#(MetadataRequest) reqFifo,
                                           FIFO#(MetadataResponse) respFifo);
    MetadataClient ret_ifc;
    ret_ifc = (interface MetadataClient;
      interface Get request = toGet(reqFifo);
      interface Put response = toPut(respFifo);
    endinterface);
    return ret_ifc;
  endfunction

  rule default_next_control_state if (defaultReqFifo.first matches tagged DefaultRequest {pkt: .pkt, meta: .meta});
    defaultReqFifo.deq;
    $display("(%0d) move default packet %x", $time, pkt, fshow(meta));
    MetadataRequest req = tagged ForwardQueueRequest {pkt: pkt, meta: meta};
    currPacketFifo.enq(req);
  endrule

  interface eventPktSend = toPipeOut(currPacketFifo);
endmodule

typedef enum {
   StateParseStart,
   StateParseEthernet,
   StateParseIpv4
} ParserState deriving (Bits, Eq);
module mkStateParseStart#(Reg#(ParserState) state, FIFOF#(EtherData) datain, Wire#(Bool) start_fsm)(Empty);
  rule load_packet if (state == StateParseStart);
    let v = datain.first;
    if (v.sop) begin
      state <= StateParseEthernet;
      start_fsm <= True;
    end
    else begin
      datain.deq;
      start_fsm <= False;
    end
  endrule
endmodule

interface ParseEthernet;

  interface Get#(Bit#(16)) parse_ipv4;
  interface Get#(Bit#(16)) parsedOut_ethernet_etherType;
  method Action start;
  method Action stop;
endinterface
module mkStateParseEthernet#(Reg#(ParserState) state, FIFOF#(EtherData) datain)(ParseEthernet);
  let verbose = True;
  FIFOF#(Bit#(16)) unparsed_parse_ipv4_fifo <- mkSizedFIFOF(1);

  FIFOF#(Bit#(16)) parsed_etherType_fifo <- mkFIFOF;

  Wire#(Bit#(128)) packet_in_wire <- mkDWire(0);
  Vector#(4, Wire#(Maybe#(ParserState))) next_state_wire <- replicateM(mkDWire(tagged Invalid));
  PulseWire start_wire <- mkPulseWire();
  PulseWire clear_wire <- mkPulseWire();
  (* fire_when_enabled *)
  rule arbitrate_outgoing_state if (state == StateParseEthernet);
    Vector#(4, Bool) next_state_valid = replicate(False);
    Bool stateSet = False;
    for (Integer port=0; port<4; port=port+1) begin
      next_state_valid[port] = isValid(next_state_wire[port]);
      if (!stateSet && next_state_valid[port]) begin
        stateSet = True;
        ParserState next_state = fromMaybe(?, next_state_wire[port]);
        state <= next_state;
      end
    end
  endrule

  function ParserState compute_next_state(Bit#(16) v);
    ParserState nextState = StateParseStart;
    case (byteSwap(v)) matches
      'h800: begin
        nextState = StateParseIpv4;
      end
      default: begin
        nextState = StateParseStart;
      end
    endcase
    return nextState;
  endfunction

  rule load_packet if (state == StateParseEthernet);
    let data_current <- toGet(datain).get;
    packet_in_wire <= data_current.data;
  endrule

  Stmt stmt_parse_ethernet =
  seq
  action
    let data_this_cycle = packet_in_wire;
    Vector#(128, Bit#(1)) dataVec = unpack(data_this_cycle);
    let hdr = extract_EthernetT(pack(takeAt(0, dataVec)));
    $display(fshow(hdr));
    Vector#(16, Bit#(1)) unparsed = takeAt(0, dataVec);
    let nextState = compute_next_state(hdr.etherType);
    if (verbose) $display("Goto state ", nextState);
    if (nextState == StateParseIpv4) begin
      unparsed_parse_ipv4_fifo.enq(pack(unparsed));
    end
    parsed_etherType_fifo.enq(hdr.etherType);
    next_state_wire[0] <= tagged Valid nextState;
  endaction
  endseq;
  FSM fsm_parse_ethernet <- mkFSM(stmt_parse_ethernet);
  rule start_fsm if (start_wire);
    fsm_parse_ethernet.start;
  endrule
  rule clear_fsm if (clear_wire);
    fsm_parse_ethernet.abort;
  endrule

  method start = start_wire.send;
  method stop = clear_wire.send;
  interface parse_ipv4 = toGet(unparsed_parse_ipv4_fifo);
  interface parsedOut_ethernet_etherType = toGet(parsed_etherType_fifo);
endmodule

interface ParseIpv4;
  interface Put#(Bit#(16)) parse_ethernet;
  interface Get#(Bit#(8)) parsedOut_ipv4_protocol;
  method Action start;
  method Action stop;
endinterface
module mkStateParseIpv4#(Reg#(ParserState) state, FIFOF#(EtherData) datain, FIFOF#(ParserState) parseStateFifo)(ParseIpv4);
  let verbose = False;
  FIFOF#(Bit#(16)) unparsed_parse_ethernet_fifo <- mkBypassFIFOF;

  FIFOF#(Bit#(8)) parsed_ipv4_protocol_fifo <- mkFIFOF;
  FIFOF#(Bit#(144)) internal_fifo_144 <- mkSizedFIFOF(1);

  Wire#(Bit#(128)) packet_in_wire <- mkDWire(0);
  Vector#(4, Wire#(Maybe#(ParserState))) next_state_wire <- replicateM(mkDWire(tagged Invalid));
  PulseWire start_wire <- mkPulseWire();
  PulseWire clear_wire <- mkPulseWire();
  (* fire_when_enabled *)
  rule arbitrate_outgoing_state if (state == StateParseIpv4);
    Vector#(4, Bool) next_state_valid = replicate(False);
    Bool stateSet = False;
    for (Integer port=0; port<4; port=port+1) begin
      next_state_valid[port] = isValid(next_state_wire[port]);
      if (!stateSet && next_state_valid[port]) begin
        stateSet = True;
        ParserState next_state = fromMaybe(?, next_state_wire[port]);
        state <= next_state;
      end
    end
  endrule

  rule load_packet if (state == StateParseIpv4);
    let data_current <- toGet(datain).get;
    packet_in_wire <= data_current.data;
  endrule

  Stmt stmt_parse_ipv4 =
  seq
  action
    let data_this_cycle = packet_in_wire;
    let data_last_cycle <- toGet(unparsed_parse_ethernet_fifo).get;
    Bit#(144) data = {data_this_cycle, data_last_cycle};
    internal_fifo_144.enq(data);
  endaction
  action
    let data_this_cycle = packet_in_wire;
    let data_last_cycle <- toGet(internal_fifo_144).get;
    Bit#(272) data = {data_this_cycle, data_last_cycle};
    Vector#(272, Bit#(1)) dataVec = unpack(data);
    let hdr = extract_Ipv4T(pack(takeAt(0, dataVec)));
    $display(fshow(hdr));
    parseStateFifo.enq(StateParseIpv4);
    parsed_ipv4_protocol_fifo.enq(hdr.protocol);
    next_state_wire[0] <= tagged Valid StateParseStart;
  endaction
  endseq;
  FSM fsm_parse_ipv4 <- mkFSM(stmt_parse_ipv4);
  rule start_fsm if (start_wire);
    fsm_parse_ipv4.start;
  endrule
  rule clear_fsm if (clear_wire);
    fsm_parse_ipv4.abort;
  endrule

  method start = start_wire.send;
  method stop = clear_wire.send;
  interface parse_ethernet = toPut(unparsed_parse_ethernet_fifo);
  interface parsedOut_ipv4_protocol = toGet(parsed_ipv4_protocol_fifo);
endmodule

interface Parser;
  interface Put#(EtherData) frameIn;
  interface Get#(MetadataT) meta;
  method ParserPerfRec read_perf_info;
endinterface
typedef 4 PortMax;
(* synthesize *)
module mkParser(Parser);
  Reg#(ParserState) curr_state <- mkReg(StateParseStart);
  Reg#(Bool) started <- mkReg(False);
  FIFOF#(EtherData) data_in_fifo <- mkFIFOF;
  Wire#(Bool) start_fsm <- mkDWire(False);

  Vector#(PortMax, FIFOF#(ParserState)) parse_state_in_fifo <- replicateM(mkGFIFOF(False, True)); // ungarded deq
  FIFOF#(ParserState) parse_state_out_fifo <- mkFIFOF;
  FIFOF#(MetadataT) metadata_out_fifo <- mkFIFOF;

  (* fire_when_enabled *)
  rule arbitrate_parse_state;
    Bool sentOne = False;
    for (Integer port=0; port<valueOf(PortMax); port=port+1) begin
      if (!sentOne && parse_state_in_fifo[port].notEmpty()) begin
        ParserState state <- toGet(parse_state_in_fifo[port]).get;
        sentOne = True;
        parse_state_out_fifo.enq(state);
      end
    end
  endrule

  Empty init_state <- mkStateParseStart(curr_state, data_in_fifo, start_fsm);
  ParseIpv4 parse_ipv4 <- mkStateParseIpv4(curr_state, data_in_fifo, parse_state_in_fifo[0]);
  ParseEthernet parse_ethernet <- mkStateParseEthernet(curr_state, data_in_fifo);
  mkConnection(parse_ipv4.parse_ethernet, parse_ethernet.parse_ipv4);
  rule start if (start_fsm);
    if (!started) begin
      parse_ipv4.start;
      parse_ethernet.start;
      started <= True;
    end
  endrule

  rule clear if (!start_fsm && curr_state == StateParseStart);
    if (started) begin
      parse_ipv4.stop;
      parse_ethernet.stop;
      started <= False;
    end
  endrule

  // handle ipv4 packet
  rule handle_ipv4_packet if (parse_state_out_fifo.first == StateParseIpv4);
    parse_state_out_fifo.deq;
    let protocol <- toGet(parse_ipv4.parsedOut_ipv4_protocol).get;
    let etherType <- toGet(parse_ethernet.parsedOut_ethernet_etherType).get;
    $display("(%0d) handle ipv4", $time);
    MetadataT meta = defaultValue;
    meta.protocol = tagged Valid protocol;
    meta.etherType = tagged Valid etherType;
    meta.valid_ipv4 = tagged Valid True;
    meta.valid_ethernet = tagged Valid True;
    metadata_out_fifo.enq(meta);
  endrule

  interface frameIn = toPut(data_in_fifo);
  interface meta = toGet(metadata_out_fifo);
endmodule

typedef enum {
  StateDeparseIdle,
  StateDeparseEthernet,
  StateDeparseIpv4
} DeparserState deriving (Bits, Eq, FShow);

function Tuple2#(EthernetT, EthernetT) toEthernet(MetadataT meta);
   EthernetT data = defaultValue;
   EthernetT mask = defaultMask;
   data.dstAddr = fromMaybe(?, meta.dstAddr);
   mask.dstAddr = 0;
   data.etherType = fromMaybe(?, meta.etherType);
   mask.etherType = 0;
   return tuple2(data, mask);
endfunction

function Tuple2#(Ipv4T, Ipv4T) toIpv4(MetadataT meta);
   Ipv4T ipv4 = defaultValue;
   Ipv4T mask = defaultMask;
   ipv4.protocol = fromMaybe(?, meta.protocol);
   mask.protocol = 0;
   return tuple2(ipv4, mask);
endfunction

module mkStateDeparseIdle#(Reg#(DeparserState) state, FIFOF#(EtherData) datain, FIFOF#(EtherData) dataout, Wire#(Bool) start_fsm)(Empty);

   rule load_packet if (state == StateDeparseIdle);
      let v = datain.first;
      if (v.sop) begin
         state <= StateDeparseEthernet;
         start_fsm <= True;
         $display("(%0d) Deparse Ethernet Start", $time);
      end
      else begin
         datain.deq;
         dataout.enq(v);
         $display("(%0d) payload ", $time, fshow(v));
         start_fsm <= False;
      end
   endrule
endmodule

interface DeparseEthernet;
  interface Get#(EtherData) deparse_ipv4;
  method Action start;
  method Action clear;
endinterface
module mkStateDeparseEthernet#(Reg#(DeparserState) state, FIFOF#(EtherData) datain, FIFOF#(EtherData) dataout, FIFOF#(EthernetT) meta_fifo, FIFOF#(EthernetT) mask_fifo)(DeparseEthernet);
  let verbose = False;
  Wire#(EtherData) packet_in_wire <- mkDWire(defaultValue);

  FIFO#(EtherData) deparse_ipv4_fifo <- mkFIFO;
  PulseWire start_wire <- mkPulseWire;
  PulseWire clear_wire <- mkPulseWire;

  function DeparserState compute_next_state(Bit#(16) v);
    DeparserState nextState = StateDeparseIdle;
    case (byteSwap(v)) matches
      'h800: begin
        nextState = StateDeparseIpv4;
      end
      default: begin
        nextState = StateDeparseIdle;
      end
    endcase
    return nextState;
  endfunction

  rule load_packet if (state == StateDeparseEthernet);
    let data_current <- toGet(datain).get;
    packet_in_wire <= data_current;
  endrule
  Stmt deparse_ethernet =
  seq
  action
    let data_this_cycle = packet_in_wire;
    Vector#(16, Bit#(1)) unused = takeAt(112, unpack(data_this_cycle.data));
    Vector#(112, Bit#(1)) data = takeAt(0, unpack(data_this_cycle.data));
    Vector#(112, Bit#(1)) curr_meta = takeAt(0, unpack(byteSwap(pack(meta_fifo.first))));
    Vector#(112, Bit#(1)) curr_mask = takeAt(0, unpack(byteSwap(pack(mask_fifo.first))));
    let masked_data = pack(data) & pack(curr_mask);
    let curr_data = masked_data | pack(curr_meta);
    EthernetT ethernet_t = unpack(pack(masked_data));
    data_this_cycle.data = {pack(unused), pack(curr_data)};
    let nextState = compute_next_state(ethernet_t.etherType);
    state <= nextState;
    if (nextState == StateDeparseIpv4) begin
      deparse_ipv4_fifo.enq(data_this_cycle);
    end
    meta_fifo.deq;
    mask_fifo.deq;
  endaction
  endseq;

  FSM fsm_deparse_ethernet <- mkFSM(deparse_ethernet);
  rule start_fsm if (start_wire);
    fsm_deparse_ethernet.start;
  endrule
  rule clear_fsm if (clear_wire);
    fsm_deparse_ethernet.abort;
  endrule
  method start = start_wire.send;
  method clear = clear_wire.send;
  interface deparse_ipv4 = toGet(deparse_ipv4_fifo);
endmodule

interface DeparseIpv4;
  interface Put#(EtherData) deparse_ethernet;
  method Action start;
  method Action clear;
endinterface
module mkStateDeparseIpv4#(Reg#(DeparserState) state, FIFOF#(EtherData) datain, FIFOF#(EtherData) dataout, FIFOF#(Ipv4T) meta_fifo, FIFOF#(Ipv4T) mask_fifo)(DeparseIpv4);
  let verbose = False;
  Wire#(EtherData) packet_in_wire <- mkDWire(defaultValue);
  FIFOF#(EtherData) deparse_ethernet_fifo <- mkBypassFIFOF;
  PulseWire start_wire <- mkPulseWire;
  PulseWire clear_wire <- mkPulseWire;

  rule load_packet if (state == StateDeparseIpv4 && !deparse_ethernet_fifo.notEmpty());
    let data_current <- toGet(datain).get;
    packet_in_wire <= data_current;
    $display("(%0d) IPv4: ", $time, fshow(data_current));
  endrule
  Stmt deparse_ipv4 =
  seq
  action
    let data_this_cycle <- toGet(deparse_ethernet_fifo).get;
    Vector#(112, Bit#(1)) unused = takeAt(0, unpack(data_this_cycle.data));
    Vector#(16, Bit#(1)) data = takeAt(112, unpack(data_this_cycle.data));
    Vector#(16, Bit#(1)) curr_meta = takeAt(0, unpack(byteSwap(pack(meta_fifo.first))));
    Vector#(16, Bit#(1)) curr_mask = takeAt(0, unpack(byteSwap(pack(mask_fifo.first))));
    let masked_data = pack(data) & pack(curr_mask);
    let curr_data = masked_data | pack(curr_meta);
    $display("(%0d) IPv4: [1] ", $time, fshow(data_this_cycle), " meta=%h, mask=%h", curr_mask, curr_meta);
    data_this_cycle.data = {pack(curr_data), pack(unused)};
    dataout.enq(data_this_cycle);
  endaction
  action
    let data_this_cycle = packet_in_wire;
    Vector#(128, Bit#(1)) data = takeAt(0, unpack(data_this_cycle.data));
    Vector#(128, Bit#(1)) curr_meta = takeAt(16, unpack(byteSwap(pack(meta_fifo.first))));
    Vector#(128, Bit#(1)) curr_mask = takeAt(16, unpack(byteSwap(pack(mask_fifo.first))));
    let masked_data = pack(data) & pack(curr_mask);
    let curr_data = masked_data | pack(curr_meta);
    data_this_cycle.data = {pack(curr_data)};
    dataout.enq(data_this_cycle);
    $display("(%0d) IPv4: [2] ", $time, fshow(data_this_cycle));
  endaction
  action
    let data_this_cycle = packet_in_wire;
    Vector#(112, Bit#(1)) unused = takeAt(16, unpack(data_this_cycle.data));
    Vector#(16, Bit#(1)) data = takeAt(0, unpack(data_this_cycle.data));
    Vector#(16, Bit#(1)) curr_meta = takeAt(144, unpack(byteSwap(pack(meta_fifo.first))));
    Vector#(16, Bit#(1)) curr_mask = takeAt(144, unpack(byteSwap(pack(mask_fifo.first))));
    let masked_data = pack(data) & pack(curr_mask);
    let curr_data = masked_data | pack(curr_meta);
    Ipv4T ipv4_t = unpack(pack(masked_data));
    data_this_cycle.data = {pack(unused), pack(curr_data)};
    $display("(%0d) compute_next_state protocol", $time, fshow(meta_fifo.first.protocol));
    $display("(%0d) Goto ", $time, fshow(nextState));
    dataout.enq(data_this_cycle);
    state <= StateDeparseIdle;
    meta_fifo.deq;
    mask_fifo.deq;
  endaction
  endseq;

  FSM fsm_deparse_ipv4 <- mkFSM(deparse_ipv4);
  rule start_fsm if (start_wire);
    fsm_deparse_ipv4.start;
  endrule
  rule clear_fsm if (clear_wire);
    fsm_deparse_ipv4.abort;
  endrule
  method start = start_wire.send;
  method clear = clear_wire.send;
  interface deparse_ethernet = toPut(deparse_ethernet_fifo);
endmodule

interface Deparser;
  interface PipeIn#(MetadataT) metadata;
  interface PktWriteServer writeServer;
  interface PktWriteClient writeClient;
  method DeparserPerfRec read_perf_info;
endinterface
(* synthesize *)
module mkDeparser(Deparser);
  let verbose = False;
  FIFOF#(EtherData) data_in_fifo <- mkSizedFIFOF(4);
  FIFOF#(EtherData) data_out_fifo <- mkFIFOF;
  FIFOF#(MetadataT) metadata_in_fifo <- mkFIFOF;
  Reg#(Bool) started <- mkReg(False);
  Wire#(Bool) start_fsm <- mkDWire(False);
  Reg#(DeparserState) curr_state <- mkReg(StateDeparseIdle);

  Vector#(PortMax, FIFOF#(DeparserState)) deparse_state_in_fifo <- replicateM(mkGFIFOF(False, True));
  FIFOF#(DeparserState) deparse_state_out_fifo <- mkFIFOF;

  FIFOF#(EthernetT) ethernet_meta_fifo <- mkFIFOF;
  FIFOF#(Ipv4T) ipv4_meta_fifo <- mkFIFOF;

  FIFOF#(EthernetT) ethernet_mask_fifo <- mkFIFOF;
  FIFOF#(Ipv4T) ipv4_mask_fifo <- mkFIFOF;

  Reg#(Bit#(32)) clk_cnt <- mkReg(0);
  Reg#(Bit#(32)) deparser_start_time <- mkReg(0);
  Reg#(Bit#(32)) deparser_end_time <- mkReg(0);
  rule clockrule;
    clk_cnt <= clk_cnt + 1;
  endrule

  (* fire_when_enabled *)
  rule arbitrate_deparse_state;
    Bool sentOne = False;
    for (Integer port = 0; port < valueOf(PortMax); port = port+1) begin
      if (!sentOne && deparse_state_in_fifo[port].notEmpty()) begin
        DeparserState state <- toGet(deparse_state_in_fifo[port]).get();
        sentOne = True;
        $display("(%0d) xxx arbitrate %h", $time, port);
        deparse_state_out_fifo.enq(state);
      end
    end
  endrule

  rule get_metadata;
     let v <- toGet(metadata_in_fifo).get;
     let ethernet = toEthernet(v);
     match {.data, .mask} = ethernet;
     ethernet_meta_fifo.enq(data);
     ethernet_mask_fifo.enq(mask);

     let ipv4 = toIpv4(v);
     match {.ipv4_data, .ipv4_mask} = ipv4;
     if (verbose) $display("(%0d) ipv4 meta", $time, fshow(ipv4));
     ipv4_meta_fifo.enq(ipv4_data);
     ipv4_mask_fifo.enq(ipv4_mask);
  endrule

  Empty init_state <- mkStateDeparseIdle(curr_state, data_in_fifo, data_out_fifo, start_fsm);
  DeparseEthernet deparse_ethernet <- mkStateDeparseEthernet(curr_state, data_in_fifo, data_out_fifo, ethernet_meta_fifo, ethernet_mask_fifo);
  DeparseIpv4 deparse_ipv4 <- mkStateDeparseIpv4(curr_state, data_in_fifo, data_out_fifo, ipv4_meta_fifo, ipv4_mask_fifo);
  mkConnection(deparse_ethernet.deparse_ipv4,deparse_ipv4.deparse_ethernet);

  rule start if (start_fsm);
    if (!started) begin
      deparse_ethernet.start;
      deparse_ipv4.start;
      started <= True;
      deparser_start_time <= clk_cnt;
    end
  endrule
  rule clear if (!start_fsm && curr_state == StateDeparseIdle);
    if (started) begin
      deparse_ethernet.clear;
      deparse_ipv4.clear;
      started <= False;
      deparser_end_time <= clk_cnt;
    end
  endrule
  interface PktWriteServer writeServer;
    interface writeData = toPut(data_in_fifo);
  endinterface
  interface PktWriteClient writeClient;
    interface writeData = toGet(data_out_fifo);
  endinterface
  interface metadata = toPipeIn(metadata_in_fifo);
  method DeparserPerfRec read_perf_info;
    return DeparserPerfRec {
      deparser_start_time: deparser_start_time,
      deparser_end_time: deparser_end_time
    };
  endmethod
endmodule


bluespec compilation fixes

bluespec compilation fixes

import ClientServer::*;
import RS232::*;
import Vector::*;
import GetPut::*;
import Vector::*;
import BuildVector::*;
import Ehr::*;

interface RVUart;
  // Internal connections
  interface UncachedMemServer memifc;
  method Action put (Bit#(8) data);
  method Maybe#(Bit#(8)) get;

  // external connections for rx/tx (to be connected to a UART
  method Action rx (Bit#(1) x);
  method Bit#(1) tx;
  method Bit#(16) divisor;
  method Vector#(cores, Bool) receiveInterrupt;
endinterface

module mkRVUart_RV32#(Integer divisor)(RVUart)
    provisos (NumAlias#(internalAddrSize, 8));
  // Layout of memory interface (mostly derived by SiFive Freedom E300)
  // Address   Name     Description
  // 0x0000    txData   Transmit data register
  // 0x0004    rxData   Receive data register
  // 0x0008    txCtrl   Transmit control register
  // 0x000C    rxCtrl   Receive control register
  // 0x0010    div      Baud rate divisor
  Bit#(internalAddrSize) txDataAddr = 'h0;
  Bit#(internalAddrSize) rxDataAddr = 'h4;
  Bit#(internalAddrSize) txCtrlAddr = 'h8;
  Bit#(internalAddrSize) rxCtrlAddr = 'hC;
  Bit#(internalAddrSize) divAddr    = 'h10;

  // TODO: Make a circular ring buffer for that we can then pass to connectal for
  //       memory mapped interface
  // ie. txDataRing 0x000 -> index_1[31:24], index_2[23:16], index_3[15:8], index_4[7:0]
  //     txTailPtr  0x004 -> ptr to whats next in the ring buffer
  Ehr#(2, Maybe#(Tuple2#(Bool, Bit#(internalAddrSize)))) pendingReq <- mkEhr(tagged Invalid);

  // BSV specific UART configuration
  // BSV UART implementation does a few things
  // its interface:
  // interface UART#(numeric type depth);
  //   interface RS232          rs232;
  //     method sout = rXmitDataOut;
  //     method sin  = rRecvData._write;
  //   interface Get#(Bit#(8))  tx;
  //     method ActionValue#(Bit#(8)) get;
  //   interface Put#(Bit#(8))  rx;
  //     method Action put(x)
  // Get and Put have FiFos attached to the ends of them
  UART#(16) uart <- mkUART(8, EVEN, STOP_1_5, divReg);
  // Data registers
  Reg#(Bit#(32)) txDataReg <- mkReg(0);
  Reg#(Bit#(32)) rxDataReg <- mkReg(0);
  Reg#(Bit#(32)) txCtrlReg <- mkReg(0);
  Reg#(Bit#(32)) rxCtrlReg <- mkReg(0);
  Reg#(Bit#(16)) divReg <- mkReg(divsor);

  Ehr#(2, Bool) interruptReg <- mkEhr(0);

  rule doTxData (txDataReg[31] && txCtrlReg[0]);
    // There is an implicit guard on the Fifo for uart.put
    uart.put(txDataReg[7:0]);
    txDataReg[31] <= 0;
  endrule

  rule doRxData (!rxDataReg[0] && rxCtrlReg[0]);
    rxDataReg <= uart.get;
    interruptReg[0] <= True;
  endrule

  interface UncachedMemServer memifc;
    interface Put request;
      method Action put(UncachedMemReq req) if (!isValid(pendingReq[1]));
        if (req.write) begin
          case (truncate(req.addr))
            txDataAddr: txDataReg <= req.data;
            rxDataAddr: rxDataReg <= req.data;
            txCtrlAddr: txCtrlReg  <= req.data;
            rxCtrlAddr: rxCtrlReg  <= req.data;
            // You shouldn't be able to write to the divAddr
            //divAddr:    divReg     <= req.data;
            default: noAction;
          endcase
          pendingReq[1] <= tagged Valid tuple2(True, truncate(req.addr));
        end else begin
          pendingReq[1] <= tagged Valid tuple2(False, truncate(req.addr));
        end
      endmethod
    endinterface
    interface Get response;
      method ActionValue#(UncachedMemResp) get if (pendingReq[0] matches tagged Valid .reqTuple);
        Bool write = tpl_1(reqTuple);
        Bit#(internalAddrSize) addr = tpl_2(reqTuple);
        Bit#(32) retVal = 0;
        case (truncate(addr))
          txDataAddr: retVal = txDataReg;
          rxDataAddr: retVal = rxDataReg;
          txCtrlAddr: retVal = txCtrlReg;
          rxCtrlAddr: retVal = rxCtrlReg;
          divAddr:    retVal = divReg;
          default:    retVal = 0;
        endcase
        pendingReq[0] <= tagged Invalid;
        return UncachedMemResp{ write: write, data: write? 0: retVal };
      endmethod
    endinterface
  endinterface

  method Action put(Bit#(8) data);
    if (txDataReg[31] && txCtrlReg[0]) begin
      txDataReg <= {1, '0, data};
    end
  endmethod

  method ActionValue#(Maybe#(Bit#(8))) get;
    Bit#(8) retVal = tagged Invalid;
    if (rxDataReg[31]) begin
      retVal = tagged Valid(rxDataReg[7:0]);
      if (rxCtrlReg[0]) begin
        rxDataReg <= 0;
      end
    end
    return retVal;
  endmethod

  method Action rx (Bit#(1) x) = uart.sin;
  method Bit#(1) tx = uart.sout;
  method Bit#(16) divisor = divReg;
  method Vector#(1, Bool) receiveInterrupt = vec(receiveInterrupt);

endmodule

`include "ProcConfig.bsv"

import ClientServer::*;
import RS232::*;
import Vector::*;
import Ehr::*;

interface RVUart;
  // Internal connections
  interface UncachedMemServer memifc;
  method Action put(Bit#(8));
  method Maybe(?, Bit#(8)) get;

  // external connections for rx/tx (to be connected to a UART
  method Action rx (Bit#(1) x);
  method Action Bit#(1) tx;
  method Bit#(16) divisor;
  method Vector#(cores, Bool) receiveInterrupt;
endinterface

module mkRVUart_RV32#(Integer divisor)(RVUart)
    provisos (NumAlias#(internalAddrSize, 8));
  // Layout of memory interface (mostly derived by SiFive Freedom E300)
  // Address   Name     Description
  // 0x0000    txData   Transmit data register
  // 0x0004    rxData   Receive data register
  // 0x0008    txCtrl   Transmit control register
  // 0x000C    rxCtrl   Receive control register
  // 0x0010    div      Baud rate divisor
  Bit#(internalAddrSize) txDataAddr = 'h0;
  Bit#(internalAddrSize) rxDataAddr = 'h4;
  Bit#(internalAddrSize) txCtrlAddr = 'h8;
  Bit#(internalAddrSize) rxCtrlAddr = 'hC;
  Bit#(internalAddrSize) divAddr    = 'h10;

  // TODO: Make a circular ring buffer for that we can then pass to connectal for
  //       memory mapped interface
  // ie. txDataRing 0x000 -> index_1[31:24], index_2[23:16], index_3[15:8], index_4[7:0]
  //     txTailPtr  0x004 -> ptr to whats next in the ring buffer
  Ehr#(2, Maybe#(Tuple2#(Bool, Bit#(internalAddrSize)))) pendingReq <- mkEhr(tagged Invalid);

  // BSV specific UART configuration
  // BSV UART implementation does a few things
  // its interface:
  // interface UART#(numeric type depth);
  //   interface RS232          rs232;
  //     method sout = rXmitDataOut;
  //     method sin  = rRecvData._write;
  //   interface Get#(Bit#(8))  tx;
  //     method ActionValue#(Bit#(8)) get;
  //   interface Put#(Bit#(8))  rx;
  //     method Action put(x)
  // Get and Put have FiFos attached to the ends of them
  UART#(16) uart <- mkUART(8, EVEN, STOP_1_5, divReg);
  // Data registers
  Reg#(Bit#(32)) txDataReg <- mkReg(0);
  Reg#(Bit#(32)) rxDataReg <- mkReg(0);
  Reg#(Bit#(32)) txCtrlReg <- mkReg(0);
  Reg#(Bit#(32)) rxCtrlReg <- mkReg(0);
  Reg#(Bit#(16)) divReg <- mkReg(divsor);

  Ehr#(2, Bool) interruptReg <- mkEhr(0);

  rule doTxData (txDataReg[31] && txCtrlReg[0]);
    // There is an implicit guard on the Fifo for uart.put
    uart.put(txDataReg[7:0]);
    txDataReg[31] <= 0;
  endrule

  rule doRxData (!rxDataReg[0] && rxCtrlReg[0]);
    rxDataReg <- uart.get;
    interruptReg[0] <- True;
  endrule

  interface UncachedMemServer memifc;
    interface Put request;
      method Action put(UncachedMemReq req) if (!isValid(pendingReq[1]));
        if (req.write) begin
          case (truncate(req.addr))
            txDataAddr: txDataReg <= req.data;
            rxDataAddr: rxDataReg <= req.data;
            txCtrlAddr: txCtrlReg  <= req.data;
            rxCtrlAddr: rxCtrlReg  <= req.data;
            // You shouldn't be able to write to the divAddr
            //divAddr:    divReg     <= req.data;
            default: noAction;
          endcase
          pendingReq[1] <= tagged Valid tuple2(True, truncate(req.addr));
        end else begin
          pendingReq[1] <= tagged Valid tuple2(False, truncate(req.addr));
        end
      endmethod
    endinterface
    interface Get response;
      method ActionValue#(UncachedMemResp) get if (pendingReq[0] matches tagged Valid .reqTuple);
        Bool write = tpl_1(reqTuple);
        Bit#(internalAddrSize) addr = tpl_2(reqTuple);
        Bit#(32) retVal = 0;
        case (truncate(addr))
          txDataAddr: retVal = txDataReg;
          rxDataAddr: retVal = rxDataReg;
          txCtrlAddr: retVal = txCtrlReg;
          rxCtrlAddr: retVal = rxCtrlReg;
          divAddr:    retVal = divReg;
          default:    retVal = 0;
        endcase
        pendingReq[0] <= tagged Invalid;
        return UncachedMemResp{ write: write, data: write? 0: retVal };
      endmethod
    endinterface

    method Action put(Bit#(8) data);
      if (txDataReg[31] && txCtrlReg[0]) begin
        txDataReg <= {1, '0, data};
      end
    endmethod

    method Maybe(?, Bit#(8)) get;
      Bit#(8) retVal = tagged Invalid;
      if (rxDataReg[31]) begin
        retVal = tagged Valid(rxDataReg[7:0]);
        if (rxCtrlReg[0]) begin
          rxDataReg <= 0;
        end
      end
      return retVal;
    endmethod
endmodule