added bsv implementation of SPI

added bsv implementation of SPI

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

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

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

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

import FIFO::*;
import GetPut::*;
import Connectable :: *;
import PCIE :: *; // ConnectableWithClocks
import Clocks :: *;
import SPI :: *;

import GetPutWithClocks :: *;
import Zynq::*;
import Imageon::*;
import HDMI::*;
import AxiDMA::*;
import BlueScope::*;
import SensorToVideo::*;

interface CoreIndication;
    method Action spi_control_value(Bit#(32) v);
    method Action iserdes_control_value(Bit#(32) v);
    method Action decoder_control_value(Bit#(32) v);
    method Action triggen_control_value(Bit#(32) v);
    method Action spi_rxfifo_value(Bit#(32) v);
    method Action debugind(Bit#(32) v);
    method Action spi_response(Bit#(26) v);
endinterface

interface CoreRequest;
    method Action set_spi_control(Bit#(32) v);
    method Action get_spi_control();
    method Action set_iserdes_control(Bit#(32) v);
    method Action get_iserdes_control();
    method Action set_decoder_control(Bit#(32) v);
    method Action get_decoder_control();
    method Action set_triggen_control(Bit#(32) v);
    method Action get_triggen_control();

    method Action set_host_vita_reset(Bit#(1) v);
    method Action set_host_oe(Bit#(1) v);

    method Action set_spi_reset(Bit#(1) v);
    method Action set_spi_timing(Bit#(16) v);
    method Action put_spi_txfifo(Bit#(32) v);
    method Action get_spi_rxfifo();

    method Action set_serdes_reset(Bit#(1) v);
    method Action set_serdes_auto_align(Bit#(1) v);
    method Action set_serdes_align_start(Bit#(1) v);
    method Action set_serdes_fifo_enable(Bit#(1) v);
    method Action set_serdes_manual_tap(Bit#(10) v);
    method Action set_serdes_training(Bit#(10) v);
    method Action set_decoder_reset(Bit#(1) v);
    method Action set_decoder_enable(Bit#(1) v);
    method Action set_decoder_code_ls(Bit#(10) v);
    method Action set_decoder_code_le(Bit#(10) v);
    method Action set_decoder_code_fs(Bit#(10) v);
    method Action set_decoder_code_fe(Bit#(10) v);
    method Action set_decoder_code_bl(Bit#(10) v);
    method Action set_decoder_code_img(Bit#(10) v);
    method Action set_trigger_enable(Bit#(3) v);
    method Action set_trigger_default_freq(Bit#(32) v);
    method Action set_trigger_cnt_trigger0high(Bit#(32) v);
    method Action set_trigger_cnt_trigger0low(Bit#(32) v);
    method Action set_syncgen_delay(Bit#(16) v);
    method Action set_syncgen_hactive(Bit#(16) v);
    method Action set_syncgen_hfporch(Bit#(16) v);
    method Action set_syncgen_hsync(Bit#(16) v);
    method Action set_syncgen_hbporch(Bit#(16) v);
    method Action set_syncgen_vactive(Bit#(16) v);
    method Action set_syncgen_vfporch(Bit#(16) v);
    method Action set_syncgen_vsync(Bit#(16) v);
    method Action set_syncgen_vbporch(Bit#(16) v);
    method Action set_debugreq(Bit#(32) v);
    method Action get_debugind();
    method Action put_spi_request(Bit#(26) v);
endinterface

interface ImageCaptureIndication;
    interface CoreIndication coreIndication;
    interface BlueScopeIndication bsIndication;
endinterface

interface ImageCaptureRequest;
   interface CoreRequest coreRequest;
   interface BlueScopeRequest bsRequest;
   interface ImageonVita imageon;
   interface ImageonSensorData sensor_data;
   interface HDMI hdmi;
   interface SpiPins spi;
endinterface

module mkImageCaptureRequest#(Clock imageon_clock, Clock hdmi_clock,
    ImageCaptureIndication indication)(ImageCaptureRequest) provisos (Bits#(XsviData,xsviDataWidth));

    Clock defaultClock <- exposeCurrentClock();
    Reset defaultReset <- exposeCurrentReset();
    Reset imageon_reset <- mkAsyncReset(2, defaultReset, imageon_clock);
    Reset hdmi_reset <- mkAsyncReset(2, defaultReset, hdmi_clock);

    ImageonVitaController imageonVita <- mkImageonVitaController();
    ImageonControl control = imageonVita.control;
    ImageonXsviFromSensor xsviFromSensor <- mkImageonXsviFromSensor(imageon_clock, imageon_reset, clocked_by hdmi_clock, reset_by hdmi_reset);

    AxiDMA dma <- mkAxiDMA;
    WriteChan dma_debug_write_chan = dma.write.writeChannels[1];
    BlueScopeInternal bsi <- mkSyncBlueScopeInternal(32, dma_debug_write_chan, indication.bsIndication,
					             hdmi_clock, hdmi_reset, defaultClock, defaultReset);

    SPI#(Bit#(26)) spiController <- mkSPI(1000);

    SensorToVideo converter <- mkSensorToVideo(clocked_by hdmi_clock, reset_by hdmi_reset);
    HdmiOut hdmiOut <- mkHdmiOut(clocked_by hdmi_clock, reset_by hdmi_reset);

    mkConnection(control.rxfifo_response.get, indication.coreIndication.spi_rxfifo_value);
    // hdmi clock domain
    //mkConnection(xsviFromSensor.out, converter.in);
    rule xsviConnection;
        let xsvi <- xsviFromSensor.out.get();
        //bsi.dataIn(extend(pack(xsvi)), extend(pack(xsvi)));
        converter.in.put(xsvi);
    endrule
    // hdmi clock domain
    mkConnection(converter.out, hdmiOut.rgb);

    mkConnection(spiController.response.get(), indication.coreIndication.spi_response);

    interface CoreRequest coreRequest;
    method Action set_spi_control(Bit#(32) v);
        control.set_spi_control(v);
    endmethod
    method Action get_spi_control();
        indication.coreIndication.spi_control_value(control.get_spi_control());
    endmethod

    method Action set_iserdes_control(Bit#(32) v);
        control.set_iserdes_control(v);
    endmethod
    method Action get_iserdes_control();
        indication.coreIndication.iserdes_control_value(control.get_iserdes_control());
    endmethod

    method Action set_decoder_control(Bit#(32) v);
        control.set_decoder_control(v);
    endmethod
    method Action get_decoder_control();
        indication.coreIndication.decoder_control_value(control.get_decoder_control());
    endmethod

    method Action set_triggen_control(Bit#(32) v);
        control.set_triggen_control(v);
    endmethod
    method Action get_triggen_control();
        indication.coreIndication.triggen_control_value(control.get_triggen_control());
    endmethod

    method Action set_host_vita_reset(Bit#(1) v);
        control.set_host_vita_reset(v);
    endmethod
    method Action set_host_oe(Bit#(1) v);
        control.set_host_oe(v);
    endmethod

    method Action set_spi_reset(Bit#(1) v);
        control.set_spi_reset(v);
    endmethod
    method Action set_spi_timing(Bit#(16) v);
        control.set_spi_timing(v);
    endmethod
    method Action put_spi_txfifo(Bit#(32) v);
        control.txfifo.put(v);
    endmethod
    method Action get_spi_rxfifo();
        control.rxfifo_request.put(32'hABBAABBA);
    endmethod

    method Action set_serdes_reset(Bit#(1) v);
        control.set_serdes_reset(v);
    endmethod
    method Action set_serdes_auto_align(Bit#(1) v);
        control.set_serdes_auto_align(v);
    endmethod
    method Action set_serdes_align_start(Bit#(1) v);
        control.set_serdes_align_start(v);
    endmethod
    method Action set_serdes_fifo_enable(Bit#(1) v);
        control.set_serdes_fifo_enable(v);
    endmethod
    method Action set_serdes_manual_tap(Bit#(10) v);
        control.set_serdes_manual_tap(v);
    endmethod
    method Action set_serdes_training(Bit#(10) v);
        control.set_serdes_training(v);
    endmethod
    method Action set_decoder_reset(Bit#(1) v);
        control.set_decoder_reset(v);
    endmethod
    method Action set_decoder_enable(Bit#(1) v);
        control.set_decoder_enable(v);
    endmethod
    method Action set_decoder_code_ls(Bit#(10) v);
        control.set_decoder_code_ls(v);
    endmethod
    method Action set_decoder_code_le(Bit#(10) v);
        control.set_decoder_code_le(v);
    endmethod
    method Action set_decoder_code_fs(Bit#(10) v);
        control.set_decoder_code_fs(v);
    endmethod
    method Action set_decoder_code_fe(Bit#(10) v);
        control.set_decoder_code_fe(v);
    endmethod
    method Action set_decoder_code_bl(Bit#(10) v);
        control.set_decoder_code_bl(v);
    endmethod
    method Action set_decoder_code_img(Bit#(10) v);
        control.set_decoder_code_img(v);
    endmethod
    method Action set_trigger_enable(Bit#(3) v);
        control.set_trigger_enable(v);
    endmethod
    method Action set_trigger_default_freq(Bit#(32) v);
        control.set_trigger_default_freq(v);
    endmethod
    method Action set_trigger_cnt_trigger0high(Bit#(32) v);
        control.set_trigger_cnt_trigger0high(v);
    endmethod
    method Action set_trigger_cnt_trigger0low(Bit#(32) v);
        control.set_trigger_cnt_trigger0low(v);
    endmethod
    method Action set_syncgen_delay(Bit#(16) v);
        control.set_syncgen_delay(v);
    endmethod
    method Action set_syncgen_hactive(Bit#(16) v);
        control.set_syncgen_hactive(v);
    endmethod
    method Action set_syncgen_hfporch(Bit#(16) v);
        control.set_syncgen_hfporch(v);
    endmethod
    method Action set_syncgen_hsync(Bit#(16) v);
        control.set_syncgen_hsync(v);
    endmethod
    method Action set_syncgen_hbporch(Bit#(16) v);
        control.set_syncgen_hbporch(v);
    endmethod
    method Action set_syncgen_vactive(Bit#(16) v);
        control.set_syncgen_vactive(v);
    endmethod
    method Action set_syncgen_vfporch(Bit#(16) v);
        control.set_syncgen_vfporch(v);
    endmethod
    method Action set_syncgen_vsync(Bit#(16) v);
        control.set_syncgen_vsync(v);
    endmethod
    method Action set_syncgen_vbporch(Bit#(16) v);
        control.set_syncgen_vbporch(v);
    endmethod
    method Action set_debugreq(Bit#(32) v);
        control.set_debugreq(v);
    endmethod
    method Action get_debugind();
        indication.coreIndication.debugind(control.get_debugind());
    endmethod
    method Action put_spi_request(Bit#(26) v);
        spiController.request.put(v);
    endmethod

    endinterface
   interface BlueScopeRequest bsRequest = bsi.requestIfc;
   interface ImageonVita imageon = imageonVita.host;
   interface ImageonSensorData sensor_data = xsviFromSensor.in;
   interface HDMI hdmi = hdmiOut.hdmi;
   interface SpiPins spi = spiController.pins;
endmodule

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

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

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

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

import FIFO::*;
import GetPut::*;
import Connectable :: *;
import PCIE :: *; // ConnectableWithClocks
import Clocks :: *;

import GetPutWithClocks :: *;
import Zynq::*;
import Imageon::*;
import HDMI::*;
import AxiDMA::*;
import BlueScope::*;
import SensorToVideo::*;

interface CoreIndication;
    method Action spi_control_value(Bit#(32) v);
    method Action iserdes_control_value(Bit#(32) v);
    method Action decoder_control_value(Bit#(32) v);
    method Action triggen_control_value(Bit#(32) v);
    method Action spi_rxfifo_value(Bit#(32) v);
    method Action debugind(Bit#(32) v);
endinterface

interface CoreRequest;
    method Action set_spi_control(Bit#(32) v);
    method Action get_spi_control();
    method Action set_iserdes_control(Bit#(32) v);
    method Action get_iserdes_control();
    method Action set_decoder_control(Bit#(32) v);
    method Action get_decoder_control();
    method Action set_triggen_control(Bit#(32) v);
    method Action get_triggen_control();

    method Action set_host_vita_reset(Bit#(1) v);
    method Action set_host_oe(Bit#(1) v);

    method Action set_spi_reset(Bit#(1) v);
    method Action set_spi_timing(Bit#(16) v);
    method Action put_spi_txfifo(Bit#(32) v);
    method Action get_spi_rxfifo();

    method Action set_serdes_reset(Bit#(1) v);
    method Action set_serdes_auto_align(Bit#(1) v);
    method Action set_serdes_align_start(Bit#(1) v);
    method Action set_serdes_fifo_enable(Bit#(1) v);
    method Action set_serdes_manual_tap(Bit#(10) v);
    method Action set_serdes_training(Bit#(10) v);
    method Action set_decoder_reset(Bit#(1) v);
    method Action set_decoder_enable(Bit#(1) v);
    method Action set_decoder_code_ls(Bit#(10) v);
    method Action set_decoder_code_le(Bit#(10) v);
    method Action set_decoder_code_fs(Bit#(10) v);
    method Action set_decoder_code_fe(Bit#(10) v);
    method Action set_decoder_code_bl(Bit#(10) v);
    method Action set_decoder_code_img(Bit#(10) v);
    method Action set_trigger_enable(Bit#(3) v);
    method Action set_trigger_default_freq(Bit#(32) v);
    method Action set_trigger_cnt_trigger0high(Bit#(32) v);
    method Action set_trigger_cnt_trigger0low(Bit#(32) v);
    method Action set_syncgen_delay(Bit#(16) v);
    method Action set_syncgen_hactive(Bit#(16) v);
    method Action set_syncgen_hfporch(Bit#(16) v);
    method Action set_syncgen_hsync(Bit#(16) v);
    method Action set_syncgen_hbporch(Bit#(16) v);
    method Action set_syncgen_vactive(Bit#(16) v);
    method Action set_syncgen_vfporch(Bit#(16) v);
    method Action set_syncgen_vsync(Bit#(16) v);
    method Action set_syncgen_vbporch(Bit#(16) v);
    method Action set_debugreq(Bit#(32) v);
    method Action get_debugind();
endinterface

interface ImageCaptureIndication;
    interface CoreIndication coreIndication;
    interface BlueScopeIndication bsIndication;
endinterface

interface ImageCaptureRequest;
   interface CoreRequest coreRequest;
   interface BlueScopeRequest bsRequest;
   interface ImageonVita imageon;
   interface ImageonSensorData sensor_data;
   interface HDMI hdmi;
endinterface

module mkImageCaptureRequest#(Clock imageon_clock, Clock hdmi_clock,
    ImageCaptureIndication indication)(ImageCaptureRequest) provisos (Bits#(XsviData,xsviDataWidth));

    Clock defaultClock <- exposeCurrentClock();
    Reset defaultReset <- exposeCurrentReset();
    Reset imageon_reset <- mkAsyncReset(2, defaultReset, imageon_clock);
    Reset hdmi_reset <- mkAsyncReset(2, defaultReset, hdmi_clock);

    ImageonVitaController imageonVita <- mkImageonVitaController();
    ImageonControl control = imageonVita.control;
    ImageonXsviFromSensor xsviFromSensor <- mkImageonXsviFromSensor(imageon_clock, imageon_reset, clocked_by hdmi_clock, reset_by hdmi_reset);

    AxiDMA dma <- mkAxiDMA;
    WriteChan dma_debug_write_chan = dma.write.writeChannels[1];
    BlueScopeInternal bsi <- mkSyncBlueScopeInternal(32, dma_debug_write_chan, indication.bsIndication,
					             hdmi_clock, hdmi_reset, defaultClock, defaultReset);

    SensorToVideo converter <- mkSensorToVideo(clocked_by hdmi_clock, reset_by hdmi_reset);
    HdmiOut hdmiOut <- mkHdmiOut(clocked_by hdmi_clock, reset_by hdmi_reset);

    mkConnection(control.rxfifo_response.get, indication.coreIndication.spi_rxfifo_value);
    // hdmi clock domain
    //mkConnection(xsviFromSensor.out, converter.in);
    rule xsviConnection;
        let xsvi <- xsviFromSensor.out.get();
        //bsi.dataIn(extend(pack(xsvi)), extend(pack(xsvi)));
        converter.in.put(xsvi);
    endrule
    // hdmi clock domain
    mkConnection(converter.out, hdmiOut.rgb);

    interface CoreRequest coreRequest;
    method Action set_spi_control(Bit#(32) v);
        control.set_spi_control(v);
    endmethod
    method Action get_spi_control();
        indication.coreIndication.spi_control_value(control.get_spi_control());
    endmethod

    method Action set_iserdes_control(Bit#(32) v);
        control.set_iserdes_control(v);
    endmethod
    method Action get_iserdes_control();
        indication.coreIndication.iserdes_control_value(control.get_iserdes_control());
    endmethod

    method Action set_decoder_control(Bit#(32) v);
        control.set_decoder_control(v);
    endmethod
    method Action get_decoder_control();
        indication.coreIndication.decoder_control_value(control.get_decoder_control());
    endmethod

    method Action set_triggen_control(Bit#(32) v);
        control.set_triggen_control(v);
    endmethod
    method Action get_triggen_control();
        indication.coreIndication.triggen_control_value(control.get_triggen_control());
    endmethod

    method Action set_host_vita_reset(Bit#(1) v);
        control.set_host_vita_reset(v);
    endmethod
    method Action set_host_oe(Bit#(1) v);
        control.set_host_oe(v);
    endmethod

    method Action set_spi_reset(Bit#(1) v);
        control.set_spi_reset(v);
    endmethod
    method Action set_spi_timing(Bit#(16) v);
        control.set_spi_timing(v);
    endmethod
    method Action put_spi_txfifo(Bit#(32) v);
        control.txfifo.put(v);
    endmethod
    method Action get_spi_rxfifo();
        control.rxfifo_request.put(32'hABBAABBA);
    endmethod

    method Action set_serdes_reset(Bit#(1) v);
        control.set_serdes_reset(v);
    endmethod
    method Action set_serdes_auto_align(Bit#(1) v);
        control.set_serdes_auto_align(v);
    endmethod
    method Action set_serdes_align_start(Bit#(1) v);
        control.set_serdes_align_start(v);
    endmethod
    method Action set_serdes_fifo_enable(Bit#(1) v);
        control.set_serdes_fifo_enable(v);
    endmethod
    method Action set_serdes_manual_tap(Bit#(10) v);
        control.set_serdes_manual_tap(v);
    endmethod
    method Action set_serdes_training(Bit#(10) v);
        control.set_serdes_training(v);
    endmethod
    method Action set_decoder_reset(Bit#(1) v);
        control.set_decoder_reset(v);
    endmethod
    method Action set_decoder_enable(Bit#(1) v);
        control.set_decoder_enable(v);
    endmethod
    method Action set_decoder_code_ls(Bit#(10) v);
        control.set_decoder_code_ls(v);
    endmethod
    method Action set_decoder_code_le(Bit#(10) v);
        control.set_decoder_code_le(v);
    endmethod
    method Action set_decoder_code_fs(Bit#(10) v);
        control.set_decoder_code_fs(v);
    endmethod
    method Action set_decoder_code_fe(Bit#(10) v);
        control.set_decoder_code_fe(v);
    endmethod
    method Action set_decoder_code_bl(Bit#(10) v);
        control.set_decoder_code_bl(v);
    endmethod
    method Action set_decoder_code_img(Bit#(10) v);
        control.set_decoder_code_img(v);
    endmethod
    method Action set_trigger_enable(Bit#(3) v);
        control.set_trigger_enable(v);
    endmethod
    method Action set_trigger_default_freq(Bit#(32) v);
        control.set_trigger_default_freq(v);
    endmethod
    method Action set_trigger_cnt_trigger0high(Bit#(32) v);
        control.set_trigger_cnt_trigger0high(v);
    endmethod
    method Action set_trigger_cnt_trigger0low(Bit#(32) v);
        control.set_trigger_cnt_trigger0low(v);
    endmethod
    method Action set_syncgen_delay(Bit#(16) v);
        control.set_syncgen_delay(v);
    endmethod
    method Action set_syncgen_hactive(Bit#(16) v);
        control.set_syncgen_hactive(v);
    endmethod
    method Action set_syncgen_hfporch(Bit#(16) v);
        control.set_syncgen_hfporch(v);
    endmethod
    method Action set_syncgen_hsync(Bit#(16) v);
        control.set_syncgen_hsync(v);
    endmethod
    method Action set_syncgen_hbporch(Bit#(16) v);
        control.set_syncgen_hbporch(v);
    endmethod
    method Action set_syncgen_vactive(Bit#(16) v);
        control.set_syncgen_vactive(v);
    endmethod
    method Action set_syncgen_vfporch(Bit#(16) v);
        control.set_syncgen_vfporch(v);
    endmethod
    method Action set_syncgen_vsync(Bit#(16) v);
        control.set_syncgen_vsync(v);
    endmethod
    method Action set_syncgen_vbporch(Bit#(16) v);
        control.set_syncgen_vbporch(v);
    endmethod
    method Action set_debugreq(Bit#(32) v);
        control.set_debugreq(v);
    endmethod
    method Action get_debugind();
        indication.coreIndication.debugind(control.get_debugind());
    endmethod

    endinterface
   interface BlueScopeRequest bsRequest = bsi.requestIfc;
   interface ImageonVita imageon = imageonVita.host;
   interface ImageonSensorData sensor_data = xsviFromSensor.in;
   interface HDMI hdmi = hdmiOut.hdmi;
endmodule


Fixed MemoryMappedServer for a power of 2 mmio registers

// 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"

/**
 * This package contains modules to create a memory mapped server from a
 * collection of `Reg#(Bit#(32))` interfaces.
 */
package MemoryMappedServer;

import ClientServer::*;
import GetPut::*;
import Vector::*;

import Ehr::*;

import Abstraction::*;
import RVTypes::*;

/**
 * This module takes a vector of `Reg#(Bit#(32))` interfaces and creates a
 * memory mapped IO interface to these registers.
 */
module mkMemoryMappedServer#(Vector#(n, Reg#(Bit#(XLEN))) regs)(Server#(UncachedMemReq, UncachedMemResp)) provisos (Add#(a__, TLog#(n), 64));
    Ehr#(2, Maybe#(Tuple2#(Bool, Bit#(TLog#(n))))) pendingReq <- mkEhr(tagged Invalid);

    interface Put request;
        method Action put(UncachedMemReq req) if (!isValid(pendingReq[1]));
            Bit#(TLog#(n)) index = truncate(req.addr >> valueOf(TLog#(TDiv#(XLEN,8))));
            // do write here
            if (req.write && (index <= fromInteger(valueOf(n)-1))) begin
                regs[index] <= req.data;
            end
            pendingReq[1] <= tagged Valid tuple2(req.write, index);
        endmethod
    endinterface
    interface Get response;
        method ActionValue#(UncachedMemResp) get if (pendingReq[0] matches tagged Valid {.write, .index});
            // do read here
            Bit#(XLEN) read_data = 0;
            if (!write && (index <= fromInteger(valueOf(n)-1))) begin
                read_data = regs[index];
            end
            pendingReq[0] <= tagged Invalid;
            return UncachedMemResp{ write: write, data: read_data };
        endmethod
    endinterface
endmodule

endpackage

// 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"

/**
 * This package contains modules to create a memory mapped server from a
 * collection of `Reg#(Bit#(32))` interfaces.
 */
package MemoryMappedServer;

import ClientServer::*;
import GetPut::*;
import Vector::*;

import Ehr::*;

import Abstraction::*;
import RVTypes::*;

/**
 * This module takes a vector of `Reg#(Bit#(32))` interfaces and creates a
 * memory mapped IO interface to these registers.
 */
module mkMemoryMappedServer#(Vector#(n, Reg#(Bit#(XLEN))) regs)(Server#(UncachedMemReq, UncachedMemResp)) provisos (Add#(a__, TLog#(n), 64));
    Ehr#(2, Maybe#(Tuple2#(Bool, Bit#(TLog#(n))))) pendingReq <- mkEhr(tagged Invalid);

    interface Put request;
        method Action put(UncachedMemReq req) if (!isValid(pendingReq[1]));
            Bit#(TLog#(n)) index = truncate(req.addr >> valueOf(TLog#(TDiv#(XLEN,8))));
            // do write here
            if (req.write && (index < fromInteger(valueOf(n)))) begin
                regs[index] <= req.data;
            end
            pendingReq[1] <= tagged Valid tuple2(req.write, index);
        endmethod
    endinterface
    interface Get response;
        method ActionValue#(UncachedMemResp) get if (pendingReq[0] matches tagged Valid {.write, .index});
            // do read here
            Bit#(XLEN) read_data = 0;
            if (!write && (index < fromInteger(valueOf(n)))) begin
                read_data = regs[index];
            end
            pendingReq[0] <= tagged Invalid;
            return UncachedMemResp{ write: write, data: read_data };
        endmethod
    endinterface
endmodule

endpackage


update P4Top

update P4Top

// Copyright (c) 2015 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 BRAMFIFO::*;
import BuildVector::*;
import Clocks::*;
import ClientServer::*;
import DefaultValue::*;
import FIFO::*;
import FIFOF::*;
import GetPut::*;
import ClientServer::*;
import Vector::*;
import Connectable::*;
import StmtFSM::*;
import Pipe::*;
import HostInterface::*;
import MemTypes::*;
import MemReadEngine::*;
import MemWriteEngine::*;
import MemServerIndication::*;
import MMUIndication::*;
import ConnectalMemory::*;

`ifdef MTABLE_CAM
import MatchTable_Bcam::*;
import MatchTableTypes::*;
`endif

`ifdef MTABLE_HASH
import MatchTable_Hash::*;
import MatchTableTypes::*;
`endif

import AlteraExtra::*;
import AlteraEthPhy::*;
import EthMac::*;
import ALTERA_SI570_WRAPPER::*;
import ConnectalClocks::*;
import LedController::*;
import Leds::*;
import PushButtonController::*;

import Ethernet::*;
import IngressPipeline::*;
import PacketBuffer::*;
import Parser::*;
import Types::*;
import SharedBuff::*;
import `PinTypeInclude::*;

`ifdef DEBUG_BCAM
import Bcam::*;
import BcamTypes::*;
import AsymmetricBRAM::*;
`endif

import PriorityEncoder1::*;

typedef TDiv#(`DataBusWidth, 32) WordsPerBeat;

interface P4TopIndication;
   method Action sonic_read_version_resp(Bit#(32) version);
   method Action matchTableResponse(Bit#(64) key, Bit#(32) value);
   method Action cam_search_result(Bit#(64) data);
   method Action read_setram_result(Bit#(64) data);
endinterface

interface P4TopRequest;
   method Action sonic_read_version();
   method Action writePacketData(Vector#(2, Bit#(64)) data, Bit#(1) sop, Bit#(1) eop);
   method Action readPacketBuffer(Bit#(16) addr);
   method Action writePacketBuffer(Bit#(16) addr, Bit#(64) data);
   method Action camInsert(Bit#(32) addr, Bit#(64) data);
   method Action camSearch(Bit#(64) data);
   method Action writeSetRam(Bit#(32) addr, Bit#(64) data);
   method Action readSetRam(Bit#(32) addr);
   method Action matchTableInsert(Bit#(32) key, Bit#(32) ops);
   method Action matchTableRequest(Bit#(64) key, Bit#(32) value, Bit#(32) op);
   method Action port_mapping_add_entry(Bit#(32) table_name, MatchInput_port_mapping match_key);
   method Action port_mapping_set_default_action(Bit#(32) table_name);
   method Action port_mapping_delete_entry(Bit#(32) table_name, Bit#(32) id);
   method Action port_mapping_modify_entry(Bit#(32) table_name, Bit#(32) id, ActionInput_port_mapping actions);
   method Action bd_add_entry(Bit#(32) table_name, MatchInput_bd match_key);
   method Action bd_set_default_action(Bit#(32) table_name);
   method Action bd_delete_entry(Bit#(32) table_name, Bit#(32) id);
   method Action bd_modify_entry(Bit#(32) table_name, Bit#(32) id, ActionInput_bd actions);
   method Action ipv4_fib_add_entry(Bit#(32) table_name, MatchInput_ipv4_fib match_key);
   method Action ipv4_fib_set_default_action(Bit#(32) table_name);
   method Action ipv4_fib_delete_entry(Bit#(32) table_name, Bit#(32) id);
   method Action ipv4_fib_modify_entry(Bit#(32) table_name, Bit#(32) id, ActionInput_ipv4_fib actions);
   method Action ipv4_fib_lpm_add_entry(Bit#(32) table_name, MatchInput_ipv4_fib_lpm match_key);
   method Action ipv4_fib_lpm_set_default_action(Bit#(32) table_name);
   method Action ipv4_fib_lpm_delete_entry(Bit#(32) table_name, Bit#(32) id);
   method Action ipv4_fib_lpm_modify_entry(Bit#(32) table_name, Bit#(32) id, ActionInput_ipv4_fib_lpm actions);
   method Action nexthop_add_entry(Bit#(32) table_name, MatchInput_nexthop match_key);
   method Action nexthop_set_default_action(Bit#(32) table_name);
   method Action nexthop_delete_entry(Bit#(32) table_name, Bit#(32) id);
   method Action nexthop_modify_entry(Bit#(32) table_name, Bit#(32) id, ActionInput_nexthop actions);
   method Action rewrite_mac_add_entry(Bit#(32) table_name, MatchInput_rewrite_mac match_key);
   method Action rewrite_mac_set_default_action(Bit#(32) table_name);
   method Action rewrite_mac_delete_entry(Bit#(32) table_name, Bit#(32) id);
   method Action rewrite_mac_modify_entry(Bit#(32) table_name, Bit#(32) id, ActionInput_rewrite_mac actions);
endinterface

interface P4Top;
   interface P4TopRequest request;
   interface `PinType pins;
endinterface

//module mkP4Top#(Clock derivedClock, Reset derivedReset, P4TopIndication indication)(P4Top);
module mkP4Top#(P4TopIndication indication, ConnectalMemory::MemServerIndication memServerIndication, ConnectalMemory::MMUIndication mmuIndication)(P4Top);
//module mkP4Top#(P4TopIndication indication)(P4Top);
   Clock defaultClock <- exposeCurrentClock();
   Reset defaultReset <- exposeCurrentReset();

`ifndef SIMULATION
   B2C iclock_50 <- mkB2C();
   B2C1 iclock_644 <- mkB2C1();

   Vector#(4, PushButtonController) buttons <- replicateM(mkPushButtonController(iclock_50.c, clocked_by iclock_50.c, reset_by noReset));
   for (Integer i=0; i<4; i=i+1) begin
      rule setPushButton;
         buttons[i].setRepeatParams(10, 10);
      endrule
   end

   Reset rst_644   <- mkResetInverter(iclock_50.r, clocked_by iclock_644.c);
   Reset rst_644_n <- mkAsyncReset(2, iclock_50.r, iclock_644.c);
   // PLL Input:   SFP REFCLK from SI570
   // PLL Output:  156.25MHz
   // PLL Reset:   Active High, must invert default Reset
   PLL156 pll156 <- mkPLL156(iclock_644.c, rst_644, clocked_by iclock_644.c, reset_by rst_644);
   Clock clk_156_25 = pll156.outclk_0;
   Reset rst_156   <- mkResetInverter(iclock_50.r, clocked_by clk_156_25);
   Reset rst_156_n <- mkAsyncReset(1, rst_156, clk_156_25);
   Si570Wrap si570 <- mkSi570Wrap(iclock_50.c, iclock_50.r, clocked_by iclock_50.c, reset_by iclock_50.r);

   rule si570_connections;
      let ifreq_mode = 3'b110;  //644.53125 MHZ
      si570.ifreq.mode(ifreq_mode);
      si570.istart.go(pack(buttons[1].pressed));
   endrule

`ifdef DEBUG_ETH
   //Reset xcvr_reset_n <- mkAsyncReset(2, iclock_50.r, eth.ifcs.clk_xcvr[0]);
   LedController eth_tx_led <- mkLedController(False, clocked_by clk_156_25, reset_by rst_156_n);
   LedController eth_rx_led <- mkLedController(False, clocked_by clk_156_25, reset_by rst_156_n);
   //LedController eth_rx_led <- mkLedController(False, clocked_by eth.ifcs.clk_xcvr[0], reset_by xcvr_reset_n);
   rule led_eth_tx;
      eth_tx_led.setPeriod(led_off, 500, led_on_max, 500);
   endrule
   rule led_eth_rx;
      eth_rx_led.setPeriod(led_off, 500, led_on_max, 500);
   endrule
`endif

   LedController mgmt_led <- mkLedController(False, clocked_by iclock_50.c, reset_by iclock_50.r);
   LedController sfp_led <- mkLedController(False, clocked_by iclock_644.c, reset_by rst_644_n);
   rule led_pcie;
      mgmt_led.setPeriod(led_off, 500, led_on_max, 500);
   endrule
   rule led_sfp;
      sfp_led.setPeriod(led_off, 500, led_on_max, 500);
   endrule

   Vector#(4, LedController) led_xcvr_ <- replicateM(mkLedController(False, clocked_by iclock_50.c, reset_by iclock_50.r));
   Led#(4) led_xcvr = combineLeds(led_xcvr_);

   rule led_xcvr_ready;
      for (Integer i = 0; i<4; i=i+1) begin
         led_xcvr_[i].setPeriod(led_off, 500, led_on_max, 500);
      end
   endrule

`ifdef DEBUG_ETH
   EthPhyIfc phy <- mkAlteraEthPhy(iclock_50.c, iclock_644.c, clk_156_25, iclock_50.r, clocked_by clk_156_25, reset_by rst_156_n);
   Clock xgmii_rx_clk = phy.rx_clkout;
   EthMacIfc mac <- mkEthMac(iclock_50.c, clk_156_25, replicate(xgmii_rx_clk), rst_156_n, clocked_by clk_156_25, reset_by rst_156_n);
   mapM(uncurry(mkConnection), zip(mac.tx, phy.tx));
   mapM(uncurry(mkConnection), zip(phy.rx, mac.rx));
`endif

`endif //SIMULATION

   let verbose = True;
   Reg#(Cycle_t) cycle <- mkReg(defaultValue);
   rule every1 if (verbose);
      cycle.cnt <= cycle.cnt + 1;
   endrule

   Reg#(Standard_metadata_t) standard_metadata <- mkReg(defaultValue);
   Reg#(Ingress_metadata_t) ingress_metadata <- mkReg(defaultValue);

   PacketBuffer rxPktBuff <- mkPacketBuffer();
   Parser parser <- mkParser();
   Pipeline_port_mapping ingress_port_mapping <- mkIngressPipeline_port_mapping();

`ifdef MTABLE_HASH
   Server#(RequestType, ResponseType) matchTable <- mkMatchTable_Hash();
`endif

`ifdef MTABLE_CAM
    Server#(RequestType, ResponseType) matchTable <- mkMatchTable_Bcam();
`endif

   // read client interface
   FIFO#(MemRequest) reqFifo <-mkSizedFIFO(4);
   FIFO#(MemData#(`DataBusWidth)) dataFifo <- mkSizedFIFO(32);
   MemReadClient#(`DataBusWidth) dmaClient = (interface MemReadClient;
      interface Get readReq = toGet(reqFifo);
      interface Put readData = toPut(dataFifo);
   endinterface);

   // write client interface
   FIFO#(MemRequest) writeReqFifo <- mkSizedFIFO(4);
   FIFO#(MemData#(`DataBusWidth)) writeDataFifo <- mkSizedFIFO(32);
   FIFO#(Bit#(MemTagSize)) writeDoneFifo <- mkSizedFIFO(4);
   MemWriteClient#(`DataBusWidth) dmaWriteClient = (interface MemWriteClient;
      interface Get writeReq = toGet(writeReqFifo);
      interface Get writeData = toGet(writeDataFifo);
      interface Put writeDone = toPut(writeDoneFifo);
   endinterface);

   SharedBuffer#(12, 128, 1) buff <- mkSharedBuffer(vec(dmaClient), vec(dmaWriteClient), memServerIndication, mmuIndication);

`ifdef DEBUG_BCAM
   BinaryCam#(1024, 9) bcam <- mkBinaryCam_1024_9();
`endif

   Reg#(Bit#(EtherLen)) pktLen <- mkReg(0);
   Reg#(Bit#(9)) rAddr_wires <- mkReg(0);

   rule packetParseStart;
      let pktLen <- rxPktBuff.readServer.readLen.get;
      rxPktBuff.readServer.readReq.put(EtherReq{len: truncate(pktLen)});
      if (verbose) $display(fshow(cycle) + fshow(" read packt ") + fshow(pktLen));
   endrule

   rule packetParseInProgress;
      let v <- rxPktBuff.readServer.readData.get;
      if (verbose) $display(fshow(cycle) + fshow(" load packet ") + fshow(v));
      parser.frameIn.enq(v);
      if (v.eop) begin
         if (verbose) $display(fshow(cycle) + fshow(" eop."));
      end
   endrule

   rule matchTableStart;
      let v <- toGet(parser.parseDone).get;
      $display("Parse Done");
      parser.parserReset();
   endrule

   rule forwardPayload;
      let v <- toGet(parser.payloadOut).get;
   endrule

   rule readData;
      let v <- toGet(dataFifo).get;
      $display("%d: Received data %x", cycle, v.data);
   endrule

   rule writeDone;
      let v <- toGet(writeDoneFifo).get;
      $display("%d: Write done", cycle);
   endrule

`ifdef DEBUG_BCAM
   rule readCam;
      let v <- bcam.readServer.response.get;
      //if (isValid(v)) begin
      indication.cam_search_result(zeroExtend(fromMaybe(0, v)));
      //end
   endrule
`endif

   //mkConnection(parser.phvOut, ingress_port_mapping.phvIn);

`ifdef MTABLE
   rule matchTableRes;
       let res <- matchTable.response.get;
       indication.matchTableResponse(zeroExtend(res.key), res.value);
   endrule
`endif

   interface P4TopRequest request;
      method Action sonic_read_version();
         let v= `NicVersion;
         indication.sonic_read_version_resp(v);
      endmethod
      method Action writePacketData(Vector#(2, Bit#(64)) data, Bit#(1) sop, Bit#(1) eop);
         EtherData beat = defaultValue;
         beat.data = pack(reverse(data));
         beat.sop = unpack(sop);
         beat.eop = unpack(eop);
         rxPktBuff.writeServer.writeData.put(beat);
      endmethod

      method Action readPacketBuffer(Bit#(16) addr);
         Bit#(ByteEnableSize) firstbe = 'hffff;
         Bit#(ByteEnableSize) lastbe = 'hffff;
         $display("readPacketbuffer");
         reqFifo.enq(MemRequest {sglId: 0, offset: 0, burstLen: 16, tag:0, firstbe: firstbe, lastbe: lastbe});
      endmethod

      method Action writePacketBuffer(Bit#(16) addr, Bit#(64) data);
         Bit#(ByteEnableSize) firstbe = 'hffff;
         Bit#(ByteEnableSize) lastbe = 'hffff;
         writeReqFifo.enq(MemRequest {sglId:0, offset:0, burstLen: 16, tag:0, firstbe: firstbe, lastbe: lastbe});

         //function Bit#(32) plusi(Integer i); return fromInteger(i); endfunction
         //Vector#(WordsPerBeat, Bit#(32)) v = genWith(plusi);
         function Bit#(8) plusi(Integer i); return fromInteger(i); endfunction
         Vector#(TMul#(4, WordsPerBeat), Bit#(8)) v = genWith(plusi);
         $display("%x", v);
         writeDataFifo.enq(MemData {data: pack(v), tag:0, last:True});
      endmethod

`ifdef MTABLE
      method Action matchTableRequest(Bit#(64) key, Bit#(32) value, Bit#(32) op);
        if (op == 0)
            matchTable.request.put(makeRequest(truncate(key), value, GET));
        else if (op == 1)
            matchTable.request.put(makeRequest(truncate(key), value, PUT));
        else if (op == 2)
            matchTable.request.put(makeRequest(truncate(key), value, UPDATE));
        else if (op == 3)
            matchTable.request.put(makeRequest(truncate(key), value, REMOVE));
      endmethod
`endif

`ifdef DEBUG_BCAM
      method Action camInsert(Bit#(32) addr, Bit#(64) data);
         bcam.writeServer.put(BcamWriteReq{addr:truncate(addr), data:truncate(data)});
      endmethod

      method Action camSearch(Bit#(64) data);
         //FIXME: BcamReadRequest
         bcam.readServer.request.put(truncate(data));
      endmethod
`endif

      // Generate fixed standard set of API function
      // In software, we should hide the details of match table different behind api
      // expose thrift-server to software to take advantage of existing thrift infra.
      // expose raw connectal cpp generated interface to test application.

      method Action port_mapping_add_entry(Bit#(32) table_name, MatchInput_port_mapping match_key);
         // write entries to table.
         // queue read flow_id
      endmethod

      method Action port_mapping_set_default_action(Bit#(32) table_name);
         // write default action to action engine
      endmethod

      method Action port_mapping_delete_entry(Bit#(32) table_name, Bit#(32) flow_id);
         // invalidate entries in table
      endmethod

      method Action port_mapping_modify_entry(Bit#(32) table_name, Bit#(32) flow_id, ActionInput_port_mapping actions);
         // enqueue match key
      endmethod

      method Action bd_add_entry(Bit#(32) table_name, MatchInput_bd match_key);
         // write entries to table.
         // queue read flow_id
      endmethod

      method Action bd_set_default_action(Bit#(32) table_name);
         // write default action to action engine
      endmethod

      method Action bd_delete_entry(Bit#(32) table_name, Bit#(32) flow_id);
         // invalidate entries in table
      endmethod

      method Action bd_modify_entry(Bit#(32) table_name, Bit#(32) flow_id, ActionInput_bd actions);
         // enqueue match key
      endmethod

      method Action ipv4_fib_add_entry(Bit#(32) table_name, MatchInput_ipv4_fib match_key);
         // write entries to table.
         // queue read flow_id
      endmethod

      method Action ipv4_fib_set_default_action(Bit#(32) table_name);
         // write default action to action engine
      endmethod

      method Action ipv4_fib_delete_entry(Bit#(32) table_name, Bit#(32) flow_id);
         // invalidate entries in table
      endmethod

      method Action ipv4_fib_modify_entry(Bit#(32) table_name, Bit#(32) flow_id, ActionInput_ipv4_fib actions);
         // enqueue match key
      endmethod

      method Action ipv4_fib_lpm_add_entry(Bit#(32) table_name, MatchInput_ipv4_fib_lpm match_key);
         // write entries to table.
         // queue read flow_id
      endmethod

      method Action ipv4_fib_lpm_set_default_action(Bit#(32) table_name);
         // write default action to action engine
      endmethod

      method Action ipv4_fib_lpm_delete_entry(Bit#(32) table_name, Bit#(32) flow_id);
         // invalidate entries in table
      endmethod

      method Action ipv4_fib_lpm_modify_entry(Bit#(32) table_name, Bit#(32) flow_id, ActionInput_ipv4_fib_lpm actions);
         // enqueue match key
      endmethod

      method Action nexthop_add_entry(Bit#(32) table_name, MatchInput_nexthop match_key);
         // write entries to table.
         // queue read flow_id
      endmethod

      method Action nexthop_set_default_action(Bit#(32) table_name);
         // write default action to action engine
      endmethod

      method Action nexthop_delete_entry(Bit#(32) table_name, Bit#(32) flow_id);
         // invalidate entries in table
      endmethod

      method Action nexthop_modify_entry(Bit#(32) table_name, Bit#(32) flow_id, ActionInput_nexthop actions);
         // enqueue match key
      endmethod

      method Action rewrite_mac_add_entry(Bit#(32) table_name, MatchInput_rewrite_mac match_key);
         // write entries to table.
         // queue read flow_id
      endmethod

      method Action rewrite_mac_set_default_action(Bit#(32) table_name);
         // write default action to action engine
      endmethod

      method Action rewrite_mac_delete_entry(Bit#(32) table_name, Bit#(32) flow_id);
         // invalidate entries in table
      endmethod

      method Action rewrite_mac_modify_entry(Bit#(32) table_name, Bit#(32) flow_id, ActionInput_rewrite_mac actions);
         // enqueue match key
      endmethod

      // -- Internal of API depends on P4 program.
      // -- Use dispatcher to send table info to corresponding table.
      // -- Invalid table tag will result in error.
   endinterface
   interface `PinType pins;
      // Clocks
`ifndef BSIM
`ifndef SIMULATION
      method Action osc_50(Bit#(1) b3d, Bit#(1) b4a, Bit#(1) b4d, Bit#(1) b7a, Bit#(1) b7d, Bit#(1) b8a, Bit#(1) b8d);
         iclock_50.inputclock(b4a);
      endmethod
      method Action sfp(Bit#(1) refclk);
         iclock_644.inputclock(refclk);
      endmethod
      method Action buttons(Bit#(4) v);
         for (Integer i=0; i<valueOf(NumPorts); i=i+1) begin
            buttons[i].ifc.button(v[i]);
         end
         iclock_50.inputreset(v[0]);
      endmethod
`ifdef DEBUG_ETH
      method Action serial_rx(Bit#(NumPorts) data);
         phy.serial.rx(data);
      endmethod
      method serial_tx_data = phy.serial.tx;
      method led0 = eth_tx_led.ifc.out;
      method led1 = eth_rx_led.ifc.out;
`endif
      method led2 = mgmt_led.ifc.out;
      method led3 = sfp_led.ifc.out;
      method led_bracket = led_xcvr.out;
      interface sfpctrl = (interface SFPCtrl;
         method Action los (Bit#(NumPorts) v); endmethod
         method Action mod0_presnt_n(Bit#(NumPorts) v); endmethod
         method ratesel0 = pack(replicate(1'b1));
         method ratesel1 = pack(replicate(1'b1));
         method txdisable = pack(replicate(1'b0));
         method Action txfault(Bit#(NumPorts) v); endmethod
      endinterface);
      interface deleteme_unused_clock = iclock_50.c;
      interface deleteme_unused_reset = iclock_50.r;
      interface deleteme_unused_clock2 = defaultClock;
      interface deleteme_unused_clock3 = clk_156_25;
`endif
`endif
   endinterface
endmodule

// Copyright (c) 2015 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 BRAMFIFO::*;
import BuildVector::*;
import Clocks::*;
import ClientServer::*;
import DefaultValue::*;
import FIFO::*;
import FIFOF::*;
import GetPut::*;
import ClientServer::*;
import Vector::*;
import Connectable::*;
import StmtFSM::*;
import Pipe::*;
import HostInterface::*;
import MemTypes::*;
import MemReadEngine::*;
import MemWriteEngine::*;
import MemServerIndication::*;
import MMUIndication::*;

`ifdef MTABLE_CAM
import MatchTable_Bcam::*;
import MatchTableTypes::*;
`endif

`ifdef MTABLE_HASH
import MatchTable_Hash::*;
import MatchTableTypes::*;
`endif

import AlteraExtra::*;
import AlteraEthPhy::*;
import EthMac::*;
import ALTERA_SI570_WRAPPER::*;
import ConnectalClocks::*;
import LedController::*;
import Leds::*;
import PushButtonController::*;

import Ethernet::*;
import IngressPipeline::*;
import PacketBuffer::*;
import Parser::*;
import Types::*;
import SharedBuff::*;
import `PinTypeInclude::*;

`ifdef DEBUG_BCAM
import Bcam::*;
import AsymmetricBRAM::*;
`endif

import PriorityEncoderEfficient::*;

typedef TDiv#(`DataBusWidth, 32) WordsPerBeat;

interface P4TopIndication;
   method Action sonic_read_version_resp(Bit#(32) version);
   method Action matchTableResponse(Bit#(64) key, Bit#(32) value);
   method Action cam_search_result(Bit#(64) data);
   method Action read_setram_result(Bit#(64) data);
endinterface

interface P4TopRequest;
   method Action sonic_read_version();
   method Action writePacketData(Vector#(2, Bit#(64)) data, Bit#(1) sop, Bit#(1) eop);
   method Action readPacketBuffer(Bit#(16) addr);
   method Action writePacketBuffer(Bit#(16) addr, Bit#(64) data);
   method Action camInsert(Bit#(32) addr, Bit#(64) data);
   method Action camSearch(Bit#(64) data);
   method Action writeSetRam(Bit#(32) addr, Bit#(64) data);
   method Action readSetRam(Bit#(32) addr);
   method Action matchTableInsert(Bit#(32) key, Bit#(32) ops);
   method Action matchTableRequest(Bit#(64) key, Bit#(32) value, Bit#(32) op);
   method Action port_mapping_add_entry(Bit#(32) table_name, MatchInput_port_mapping match_key);
   method Action port_mapping_set_default_action(Bit#(32) table_name);
   method Action port_mapping_delete_entry(Bit#(32) table_name, Bit#(32) id);
   method Action port_mapping_modify_entry(Bit#(32) table_name, Bit#(32) id, ActionInput_port_mapping actions);
   method Action bd_add_entry(Bit#(32) table_name, MatchInput_bd match_key);
   method Action bd_set_default_action(Bit#(32) table_name);
   method Action bd_delete_entry(Bit#(32) table_name, Bit#(32) id);
   method Action bd_modify_entry(Bit#(32) table_name, Bit#(32) id, ActionInput_bd actions);
   method Action ipv4_fib_add_entry(Bit#(32) table_name, MatchInput_ipv4_fib match_key);
   method Action ipv4_fib_set_default_action(Bit#(32) table_name);
   method Action ipv4_fib_delete_entry(Bit#(32) table_name, Bit#(32) id);
   method Action ipv4_fib_modify_entry(Bit#(32) table_name, Bit#(32) id, ActionInput_ipv4_fib actions);
   method Action ipv4_fib_lpm_add_entry(Bit#(32) table_name, MatchInput_ipv4_fib_lpm match_key);
   method Action ipv4_fib_lpm_set_default_action(Bit#(32) table_name);
   method Action ipv4_fib_lpm_delete_entry(Bit#(32) table_name, Bit#(32) id);
   method Action ipv4_fib_lpm_modify_entry(Bit#(32) table_name, Bit#(32) id, ActionInput_ipv4_fib_lpm actions);
   method Action nexthop_add_entry(Bit#(32) table_name, MatchInput_nexthop match_key);
   method Action nexthop_set_default_action(Bit#(32) table_name);
   method Action nexthop_delete_entry(Bit#(32) table_name, Bit#(32) id);
   method Action nexthop_modify_entry(Bit#(32) table_name, Bit#(32) id, ActionInput_nexthop actions);
   method Action rewrite_mac_add_entry(Bit#(32) table_name, MatchInput_rewrite_mac match_key);
   method Action rewrite_mac_set_default_action(Bit#(32) table_name);
   method Action rewrite_mac_delete_entry(Bit#(32) table_name, Bit#(32) id);
   method Action rewrite_mac_modify_entry(Bit#(32) table_name, Bit#(32) id, ActionInput_rewrite_mac actions);
endinterface

interface P4Top;
   interface P4TopRequest request;
   interface `PinType pins;
endinterface

//module mkP4Top#(Clock derivedClock, Reset derivedReset, P4TopIndication indication)(P4Top);
module mkP4Top#(P4TopIndication indication)(P4Top);
   Clock defaultClock <- exposeCurrentClock();
   Reset defaultReset <- exposeCurrentReset();

`ifndef BSIM
`ifndef SIMULATION
   B2C iclock_50 <- mkB2C();
   B2C1 iclock_644 <- mkB2C1();

   Vector#(4, PushButtonController) buttons <- replicateM(mkPushButtonController(iclock_50.c, clocked_by iclock_50.c, reset_by noReset));
   for (Integer i=0; i<4; i=i+1) begin
      rule setPushButton;
         buttons[i].setRepeatParams(10, 10);
      endrule
   end

   Reset rst_644   <- mkResetInverter(iclock_50.r, clocked_by iclock_644.c);
   Reset rst_644_n <- mkAsyncReset(2, iclock_50.r, iclock_644.c);
   // PLL Input:   SFP REFCLK from SI570
   // PLL Output:  156.25MHz
   // PLL Reset:   Active High, must invert default Reset
   PLL156 pll156 <- mkPLL156(iclock_644.c, rst_644, clocked_by iclock_644.c, reset_by rst_644);
   Clock clk_156_25 = pll156.outclk_0;
   Reset rst_156   <- mkResetInverter(iclock_50.r, clocked_by clk_156_25);
   Reset rst_156_n <- mkAsyncReset(1, rst_156, clk_156_25);
   Si570Wrap si570 <- mkSi570Wrap(iclock_50.c, iclock_50.r, clocked_by iclock_50.c, reset_by iclock_50.r);

   rule si570_connections;
      let ifreq_mode = 3'b110;  //644.53125 MHZ
      si570.ifreq.mode(ifreq_mode);
      si570.istart.go(pack(buttons[1].pressed));
   endrule

`ifdef DEBUG_ETH
   //Reset xcvr_reset_n <- mkAsyncReset(2, iclock_50.r, eth.ifcs.clk_xcvr[0]);
   LedController eth_tx_led <- mkLedController(False, clocked_by clk_156_25, reset_by rst_156_n);
   LedController eth_rx_led <- mkLedController(False, clocked_by clk_156_25, reset_by rst_156_n);
   //LedController eth_rx_led <- mkLedController(False, clocked_by eth.ifcs.clk_xcvr[0], reset_by xcvr_reset_n);
   rule led_eth_tx;
      eth_tx_led.setPeriod(led_off, 500, led_on_max, 500);
   endrule
   rule led_eth_rx;
      eth_rx_led.setPeriod(led_off, 500, led_on_max, 500);
   endrule
`endif

   LedController mgmt_led <- mkLedController(False, clocked_by iclock_50.c, reset_by iclock_50.r);
   LedController sfp_led <- mkLedController(False, clocked_by iclock_644.c, reset_by rst_644_n);
   rule led_pcie;
      mgmt_led.setPeriod(led_off, 500, led_on_max, 500);
   endrule
   rule led_sfp;
      sfp_led.setPeriod(led_off, 500, led_on_max, 500);
   endrule

   Vector#(4, LedController) led_xcvr_ <- replicateM(mkLedController(False, clocked_by iclock_50.c, reset_by iclock_50.r));
   Led#(4) led_xcvr = combineLeds(led_xcvr_);

   rule led_xcvr_ready;
      for (Integer i = 0; i<4; i=i+1) begin
         led_xcvr_[i].setPeriod(led_off, 500, led_on_max, 500);
      end
   endrule

`ifdef DEBUG_ETH
   EthPhyIfc phy <- mkAlteraEthPhy(iclock_50.c, iclock_644.c, clk_156_25, iclock_50.r, clocked_by clk_156_25, reset_by rst_156_n);
   Clock xgmii_rx_clk = phy.rx_clkout;
   EthMacIfc mac <- mkEthMac(iclock_50.c, clk_156_25, replicate(xgmii_rx_clk), rst_156_n, clocked_by clk_156_25, reset_by rst_156_n);
   mapM(uncurry(mkConnection), zip(mac.tx, phy.tx));
   mapM(uncurry(mkConnection), zip(phy.rx, mac.rx));
`endif

`endif //BSIM
`endif //SIMULATION

   let verbose = True;
   Reg#(Cycle_t) cycle <- mkReg(defaultValue);
   rule every1 if (verbose);
      cycle.cnt <= cycle.cnt + 1;
   endrule

   Reg#(Standard_metadata_t) standard_metadata <- mkReg(defaultValue);
   Reg#(Ingress_metadata_t) ingress_metadata <- mkReg(defaultValue);

   PacketBuffer rxPktBuff <- mkPacketBuffer();
   Parser parser <- mkParser();
   Pipeline_port_mapping ingress_port_mapping <- mkIngressPipeline_port_mapping();

`ifdef MTABLE_HASH
   Server#(RequestType, ResponseType) matchTable <- mkMatchTable_Hash();
`endif

`ifdef MTABLE_CAM
    Server#(RequestType, ResponseType) matchTable <- mkMatchTable_Bcam();
`endif

   // read client interface
   FIFO#(MemRequest) reqFifo <-mkSizedFIFO(4);
   FIFO#(MemData#(`DataBusWidth)) dataFifo <- mkSizedFIFO(32);
   MemReadClient#(`DataBusWidth) dmaClient = (interface MemReadClient;
      interface Get readReq = toGet(reqFifo);
      interface Put readData = toPut(dataFifo);
   endinterface);

   // write client interface
   FIFO#(MemRequest) writeReqFifo <- mkSizedFIFO(4);
   FIFO#(MemData#(`DataBusWidth)) writeDataFifo <- mkSizedFIFO(32);
   FIFO#(Bit#(MemTagSize)) writeDoneFifo <- mkSizedFIFO(4);
   MemWriteClient#(`DataBusWidth) dmaWriteClient = (interface MemWriteClient;
      interface Get writeReq = toGet(writeReqFifo);
      interface Get writeData = toGet(writeDataFifo);
      interface Put writeDone = toPut(writeDoneFifo);
   endinterface);

   MemServerIndicationOutput memServerIndication <- mkMemServerIndicationOutput;
   MMUIndicationOutput mmuIndication <- mkMMUIndicationOutput;
//   SharedBuffer#(12, 128, 1) buff <- mkSharedBuffer(vec(dmaClient), vec(dmaWriteClient), memServerIndication.ifc, mmuIndication.ifc);

`ifdef DEBUG_BCAM
   BinaryCam#(1024, 9) bcam <- mkBinaryCam_1024_9();
`endif

   Reg#(Bit#(EtherLen)) pktLen <- mkReg(0);
   Reg#(Bit#(9)) rAddr_wires <- mkReg(0);

   rule packetParseStart;
      let pktLen <- rxPktBuff.readServer.readLen.get;
      rxPktBuff.readServer.readReq.put(EtherReq{len: truncate(pktLen)});
      if (verbose) $display(fshow(cycle) + fshow(" read packt ") + fshow(pktLen));
   endrule

   rule packetParseInProgress;
      let v <- rxPktBuff.readServer.readData.get;
      if (verbose) $display(fshow(cycle) + fshow(" load packet ") + fshow(v));
      parser.frameIn.enq(v);
      if (v.eop) begin
         if (verbose) $display(fshow(cycle) + fshow(" eop."));
      end
   endrule

   rule matchTableStart;
      let v <- toGet(parser.parseDone).get;
      $display("Parse Done");
      parser.parserReset();
   endrule

   rule forwardPayload;
      let v <- toGet(parser.payloadOut).get;
   endrule

   rule readData;
      let v <- toGet(dataFifo).get;
      $display("%d: Received data %x", cycle, v.data);
   endrule

   rule writeDone;
      let v <- toGet(writeDoneFifo).get;
      $display("%d: Write done", cycle);
   endrule

`ifdef DEBUG_BCAM
   rule readCam;
      let v <- bcam.readServer.response.get;
      //if (isValid(v)) begin
      indication.cam_search_result(zeroExtend(fromMaybe(0, v)));
      //end
   endrule
`endif

`ifdef DEBUG_SETRAM
   rule readSetram;
      let v <- toGet(setRam.getRead).get;
      indication.read_setram_result(zeroExtend(v));
   endrule
`endif

   //mkConnection(parser.phvOut, ingress_port_mapping.phvIn);

`ifdef MTABLE
   rule matchTableRes;
       let res <- matchTable.response.get;
       indication.matchTableResponse(zeroExtend(res.key), res.value);
   endrule
`endif

   interface P4TopRequest request;
      method Action sonic_read_version();
         let v= `NicVersion;
         indication.sonic_read_version_resp(v);
      endmethod
      method Action writePacketData(Vector#(2, Bit#(64)) data, Bit#(1) sop, Bit#(1) eop);
         EtherData beat = defaultValue;
         beat.data = pack(reverse(data));
         beat.sop = unpack(sop);
         beat.eop = unpack(eop);
         rxPktBuff.writeServer.writeData.put(beat);
      endmethod

      method Action readPacketBuffer(Bit#(16) addr);
         Bit#(ByteEnableSize) firstbe = 'hff;
         Bit#(ByteEnableSize) lastbe = 'hff;
         reqFifo.enq(MemRequest {sglId: 0, offset: 0, burstLen: 16, tag:0, firstbe: firstbe, lastbe: lastbe});
      endmethod

      method Action writePacketBuffer(Bit#(16) addr, Bit#(64) data);
         Bit#(ByteEnableSize) firstbe = 'hff;
         Bit#(ByteEnableSize) lastbe = 'hff;
         writeReqFifo.enq(MemRequest {sglId:0, offset:0, burstLen:16, tag:0, firstbe: firstbe, lastbe: lastbe});

         function Bit#(32) plusi(Integer i); return fromInteger(i); endfunction
         Vector#(WordsPerBeat, Bit#(32)) v = genWith(plusi);
         writeDataFifo.enq(MemData {data: pack(v), tag:0, last:True});
      endmethod

`ifdef MTABLE
      method Action matchTableRequest(Bit#(64) key, Bit#(32) value, Bit#(32) op);
        if (op == 0)
            matchTable.request.put(makeRequest(truncate(key), value, GET));
        else if (op == 1)
            matchTable.request.put(makeRequest(truncate(key), value, PUT));
        else if (op == 2)
            matchTable.request.put(makeRequest(truncate(key), value, UPDATE));
        else if (op == 3)
            matchTable.request.put(makeRequest(truncate(key), value, REMOVE));
      endmethod
`endif

`ifdef DEBUG_BCAM
      method Action camInsert(Bit#(32) addr, Bit#(64) data);
         //FIXME: BcamWriteRequest
         bcam.writeServer.put(tuple2(truncate(addr), truncate(data)));
      endmethod

      method Action camSearch(Bit#(64) data);
         //FIXME: BcamReadRequest
         bcam.readServer.request.put(truncate(data));
      endmethod
`endif

`ifdef DEBUG_SETRAM
      method Action writeSetRam(Bit#(32) addr, Bit#(64) data);
         setRam.write(truncate(addr), truncate(data));
      endmethod

      method Action readSetRam(Bit#(32) addr);
         $display("%d: read set ram %x", fshow(cycle), addr);
         setRam.read(truncate(addr));
      endmethod
`endif

      // Generate fixed standard set of API function
      // In software, we should hide the details of match table different behind api
      // expose thrift-server to software to take advantage of existing thrift infra.
      // expose raw connectal cpp generated interface to test application.

      method Action port_mapping_add_entry(Bit#(32) table_name, MatchInput_port_mapping match_key);
         // write entries to table.
         // queue read flow_id
      endmethod

      method Action port_mapping_set_default_action(Bit#(32) table_name);
         // write default action to action engine
      endmethod

      method Action port_mapping_delete_entry(Bit#(32) table_name, Bit#(32) flow_id);
         // invalidate entries in table
      endmethod

      method Action port_mapping_modify_entry(Bit#(32) table_name, Bit#(32) flow_id, ActionInput_port_mapping actions);
         // enqueue match key
      endmethod

      method Action bd_add_entry(Bit#(32) table_name, MatchInput_bd match_key);
         // write entries to table.
         // queue read flow_id
      endmethod

      method Action bd_set_default_action(Bit#(32) table_name);
         // write default action to action engine
      endmethod

      method Action bd_delete_entry(Bit#(32) table_name, Bit#(32) flow_id);
         // invalidate entries in table
      endmethod

      method Action bd_modify_entry(Bit#(32) table_name, Bit#(32) flow_id, ActionInput_bd actions);
         // enqueue match key
      endmethod

      method Action ipv4_fib_add_entry(Bit#(32) table_name, MatchInput_ipv4_fib match_key);
         // write entries to table.
         // queue read flow_id
      endmethod

      method Action ipv4_fib_set_default_action(Bit#(32) table_name);
         // write default action to action engine
      endmethod

      method Action ipv4_fib_delete_entry(Bit#(32) table_name, Bit#(32) flow_id);
         // invalidate entries in table
      endmethod

      method Action ipv4_fib_modify_entry(Bit#(32) table_name, Bit#(32) flow_id, ActionInput_ipv4_fib actions);
         // enqueue match key
      endmethod

      method Action ipv4_fib_lpm_add_entry(Bit#(32) table_name, MatchInput_ipv4_fib_lpm match_key);
         // write entries to table.
         // queue read flow_id
      endmethod

      method Action ipv4_fib_lpm_set_default_action(Bit#(32) table_name);
         // write default action to action engine
      endmethod

      method Action ipv4_fib_lpm_delete_entry(Bit#(32) table_name, Bit#(32) flow_id);
         // invalidate entries in table
      endmethod

      method Action ipv4_fib_lpm_modify_entry(Bit#(32) table_name, Bit#(32) flow_id, ActionInput_ipv4_fib_lpm actions);
         // enqueue match key
      endmethod

      method Action nexthop_add_entry(Bit#(32) table_name, MatchInput_nexthop match_key);
         // write entries to table.
         // queue read flow_id
      endmethod

      method Action nexthop_set_default_action(Bit#(32) table_name);
         // write default action to action engine
      endmethod

      method Action nexthop_delete_entry(Bit#(32) table_name, Bit#(32) flow_id);
         // invalidate entries in table
      endmethod

      method Action nexthop_modify_entry(Bit#(32) table_name, Bit#(32) flow_id, ActionInput_nexthop actions);
         // enqueue match key
      endmethod

      method Action rewrite_mac_add_entry(Bit#(32) table_name, MatchInput_rewrite_mac match_key);
         // write entries to table.
         // queue read flow_id
      endmethod

      method Action rewrite_mac_set_default_action(Bit#(32) table_name);
         // write default action to action engine
      endmethod

      method Action rewrite_mac_delete_entry(Bit#(32) table_name, Bit#(32) flow_id);
         // invalidate entries in table
      endmethod

      method Action rewrite_mac_modify_entry(Bit#(32) table_name, Bit#(32) flow_id, ActionInput_rewrite_mac actions);
         // enqueue match key
      endmethod

      // -- Internal of API depends on P4 program.
      // -- Use dispatcher to send table info to corresponding table.
      // -- Invalid table tag will result in error.
   endinterface
   interface `PinType pins;
      // Clocks
`ifndef BSIM
`ifndef SIMULATION
      method Action osc_50(Bit#(1) b3d, Bit#(1) b4a, Bit#(1) b4d, Bit#(1) b7a, Bit#(1) b7d, Bit#(1) b8a, Bit#(1) b8d);
         iclock_50.inputclock(b4a);
      endmethod
      method Action sfp(Bit#(1) refclk);
         iclock_644.inputclock(refclk);
      endmethod
      method Action buttons(Bit#(4) v);
         for (Integer i=0; i<valueOf(NumPorts); i=i+1) begin
            buttons[i].ifc.button(v[i]);
         end
         iclock_50.inputreset(v[0]);
      endmethod
`ifdef DEBUG_ETH
      method Action serial_rx(Bit#(NumPorts) data);
         phy.serial.rx(data);
      endmethod
      method serial_tx_data = phy.serial.tx;
      method led0 = eth_tx_led.ifc.out;
      method led1 = eth_rx_led.ifc.out;
`endif
      method led2 = mgmt_led.ifc.out;
      method led3 = sfp_led.ifc.out;
      method led_bracket = led_xcvr.out;
      interface sfpctrl = (interface SFPCtrl;
         method Action los (Bit#(NumPorts) v); endmethod
         method Action mod0_presnt_n(Bit#(NumPorts) v); endmethod
         method ratesel0 = pack(replicate(1'b1));
         method ratesel1 = pack(replicate(1'b1));
         method txdisable = pack(replicate(1'b0));
         method Action txfault(Bit#(NumPorts) v); endmethod
      endinterface);
      interface deleteme_unused_clock = iclock_50.c;
      interface deleteme_unused_reset = iclock_50.r;
      interface deleteme_unused_clock2 = defaultClock;
      interface deleteme_unused_clock3 = clk_156_25;
`endif
`endif
   endinterface
endmodule


Fixed misaligned instruction fetch exception

// Copyright (c) 2016 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.

import ClientServer::*;
import Connectable::*;
import DefaultValue::*;
import FIFO::*;
import GetPut::*;
import Vector::*;

import Abstraction::*;
import RegUtil::*;
import RVRFile::*;
import RVCsrFile::*;
import RVExec::*;
import RVFpu::*;
import RVMulDiv::*;
import RVTypes::*;
import VerificationPacket::*;

import RVAlu::*;
import RVControl::*;
import RVDecode::*;
import RVMemory::*;

// This interface is the combination of FrontEnd and BackEnd
interface Core;
    method Action start(Addr startPc);
    method Action stop;

    method ActionValue#(VerificationPacket) getVerificationPacket;
    method ActionValue#(VMInfo) updateVMInfoI;
    method ActionValue#(VMInfo) updateVMInfoD;

    interface Client#(FenceReq, FenceResp) fence;
endinterface

instance Connectable#(Core, MemorySystem);
    module mkConnection#(Core core, MemorySystem mem)(Empty);
        mkConnection(core.fence, mem.fence);
        mkConnection(toGet(core.updateVMInfoI), toPut(mem.updateVMInfoI));
        mkConnection(toGet(core.updateVMInfoD), toPut(mem.updateVMInfoD));
    endmodule
endinstance

typedef enum {
    Wait,
    IMMU,
    IF,
    Dec,
    RegRead,
    Execute,
    Mem,
    WB,
    Trap,
    Trap2
} ProcState deriving (Bits, Eq, FShow);

module mkMulticycleCore#(
        Server#(RVIMMUReq, RVIMMUResp) ivat,
        Server#(RVIMemReq, RVIMemResp) ifetch,
        Server#(RVDMMUReq, RVDMMUResp) dvat,
        Server#(RVDMemReq, RVDMemResp) dmem,
        Bool ipi,
        Bool timerInterrupt,
        Data timer,
        Bool externalInterrupt,
        Data hartID
    )
        (Core);
    let verbose = False;
    File fout = stdout;

    ArchRFile rf <- mkArchRFile;
    RVCsrFile csrf <- mkRVCsrFile(hartID, timer, timerInterrupt, ipi, externalInterrupt);
    MulDivExec mulDiv <- mkBoothRoughMulDivExec;
    FpuExec fpu <- mkFpuExecPipeline;

    Reg#(Bool) running <- mkReg(False);
    Reg#(ProcState) state <- mkReg(Wait);

    Reg#(Addr) pc <- mkReg(0);
    Reg#(Maybe#(ExceptionCause)) exception <- mkReg(tagged Invalid);
    Reg#(Instruction) inst <- mkReg(0);
    Reg#(RVDecodedInst) dInst <- mkReg(unpack(0));
    Reg#(FrontEndCsrs) csrState <- mkReadOnlyReg( FrontEndCsrs { vmI: csrf.vmI, state: csrf.csrState } );

    Reg#(Data) rVal1 <- mkReg(0);
    Reg#(Data) rVal2 <- mkReg(0);
    Reg#(Data) rVal3 <- mkReg(0);
    Reg#(Data) data <- mkReg(0);
    Reg#(Data) addr <- mkReg(0);
    Reg#(Data) nextPc <- mkReg(0);

    FIFO#(Data)         toHost <- mkFIFO1;
    FIFO#(Data)         fromHost <- mkFIFO1;

    FIFO#(VerificationPacket) verificationPackets <- mkFIFO1;

    rule doInstMMU(running && state == IMMU);
        // request address translation from MMU
        ivat.request.put(pc);
        // reset states
        inst <= unpack(0);
        dInst <= unpack(0);
        exception <= tagged Invalid;
        // go to InstFetch stage
        state <= IF;
    endrule

    rule doInstFetch(state == IF);
        // I wanted notation like this:
        // let {addr: .phyPc, exception: .exMMU} = mmuResp.first;
        let resp <- ivat.response.get;
        let phyPc = resp.addr;
        let exMMU = resp.exception;

        if (!isValid(exMMU)) begin
            // no translation exception
            ifetch.request.put(phyPc);
            // go to decode stage
            state <= Dec;
        end else begin
            // translation exception (instruction access fault)
            exception <= exMMU;
            // send instruction to backend
            state <= Trap;
        end
    endrule

    rule doDecode(state == Dec);
        let fInst <- ifetch.response.get;

        let decInst = decodeInst(fInst);

        if (decInst matches tagged Valid .validDInst) begin
            // Legal instruction
            dInst <= validDInst;
        end else begin
            // Illegal instruction
            exception <= tagged Valid IllegalInst;
        end

        inst <= fInst;
        state <= isValid(decInst) ? RegRead : Trap;
    endrule

    rule doRegRead(state == RegRead);
        rVal1 <= rf.rd1(toFullRegIndex(dInst.rs1, getInstFields(inst).rs1));
        rVal2 <= rf.rd2(toFullRegIndex(dInst.rs2, getInstFields(inst).rs2));
        rVal3 <= rf.rd3(toFullRegIndex(dInst.rs3, getInstFields(inst).rs3));
        state <= Execute;
    endrule

    rule doExecute(state == Execute);
        let dataEx = 0;
        let addrEx = 0;
        let nextPcEx = pc + 4;

        Maybe#(Data) imm = getImmediate(dInst.imm, dInst.inst);
        case (dInst.execFunc) matches
            tagged Alu    .aluInst:
                begin
                    dataEx = execAluInst(aluInst, rVal1, rVal2, imm, pc);
                end
            tagged Br     .brFunc:
                begin
                    // data for jal
                    dataEx = pc + 4;
                    nextPcEx = execControl(brFunc, rVal1, rVal2, imm, pc);
                end
            tagged Mem    .memInst:
                begin
                    // data for store and AMO
                    dataEx = rVal2;
                    addrEx = addrCalc(rVal1, imm);
                    dvat.request.put(RVDMMUReq {addr: addrEx, size: memInst.size, op: (memInst.op matches tagged Mem .memOp ? memOp : St)});
                end
            tagged MulDiv .mulDivInst: mulDiv.exec(mulDivInst, rVal1, rVal2);
            tagged Fence  .fenceInst:  noAction;
            // TODO: Handle dynamic rounding mode
            tagged Fpu    .fpuInst:    fpu.exec(fpuInst, getInstFields(inst).rm, rVal1, rVal2, rVal3);
            tagged System .systemInst:
                begin
                    // data for CSR instructions
                    dataEx = fromMaybe(rVal1, imm);
                end
        endcase

        data <= dataEx;
        addr <= addrEx;
        nextPc <= nextPcEx;

        state <= dInst.execFunc matches tagged Mem .* ? Mem : WB;
    endrule

    rule doMem(state == Mem);
        let resp <- dvat.response.get;
        let pAddr = resp.addr;
        let exMMU = resp.exception;

        // TODO: make this type safe! get rid of .Mem accesses to tagged union
        if (!isValid(exMMU)) begin
            dmem.request.put( RVDMemReq {
                    op: dInst.execFunc.Mem.op,
                    size: dInst.execFunc.Mem.size,
                    isUnsigned: dInst.execFunc.Mem.isUnsigned,
                    addr: pAddr,
                    data: data } );
            state <= WB;
        end else begin
            exception <= exMMU;
            state <= Trap;
        end
    endrule

    rule doWB(state == WB);
        let dataWb = data;
        let addrWb = addr;
        let nextPcWb = nextPc;
        let fflagsWb = 0;
        let exceptionWB = exception;

        case(dInst.execFunc) matches
            tagged MulDiv .*: begin
                    dataWb = mulDiv.result_data();
                    mulDiv.result_deq;
                end
            tagged Fpu .*: begin
                    let fpuResult = toFullResult(fpu.result_data);
                    dataWb = fpuResult.data;
                    fflagsWb = fpuResult.fflags;
                    fpu.result_deq;
                end
            tagged Mem .memInst:
                begin
                    if (getsResponse(memInst.op)) begin
                        dataWb <- dmem.response.get;
                    end
                end
        endcase

        Maybe#(TrapCause) trap = tagged Invalid;
        if (exceptionWB matches tagged Valid .validException) begin
            trap = tagged Valid (tagged Exception validException);
        end else if ((nextPcWb & 'b011) != 0) begin
            trap = tagged Valid (tagged Exception InstAddrMisaligned);
        end

        if (dInst.execFunc matches tagged Mem .*) begin
            // don't bother checking for interrupts since you just did a memory operation
        end else begin
            // check for interrupts (this overwrites current exceptions)
            if (csrf.readyInterrupt matches tagged Valid .validInterrupt) begin
                trap = tagged Valid (tagged Interrupt validInterrupt);
            end
        end
        Bool checkForInterrupts = dInst.execFunc matches tagged Mem .* ? False : True;
        Bool extensionDirty = False;
        Bool fpuDirty = (dInst.dst == tagged Valid Fpu);
        let csrfResult <- csrf.wr(
                pc,
                // performing system instructions
                dInst.execFunc matches tagged System .sysInst ? tagged Valid sysInst : tagged Invalid,
                getInstFields(inst).csr,
                dataWb, // either rf[rs1] or zimm, computed in basicExec
                addrWb,
                // handling exceptions
                trap,
                // indirect writes
                fflagsWb,
                fpuDirty,
                extensionDirty);

        Maybe#(Addr) maybeNextPc = tagged Invalid;
        Maybe#(Data) maybeData = tagged Invalid;
        Maybe#(TrapCause) maybeTrap = tagged Invalid;
        case (csrfResult) matches
            tagged Exception .exc:
                begin
                    maybeNextPc = tagged Valid exc.trapHandlerPC;
                    maybeTrap = tagged Valid exc.exception;
                end
            tagged RedirectPC .newPc:
                maybeNextPc = tagged Valid newPc;
            tagged CsrData .data:
                maybeData = tagged Valid data;
            tagged None:
                noAction;
        endcase

        // send verification packet
        verificationPackets.enq( VerificationPacket {
                skippedPackets: 0,
                pc: pc,
                nextPc: fromMaybe(nextPcWb, maybeNextPc),
                data: fromMaybe(dataWb, maybeData),
                addr: addr,
                instruction: inst,
                dst: {pack(dInst.dst), getInstFields(inst).rd},
                trap: isValid(maybeTrap),
                trapType: (case (fromMaybe(unpack(0), maybeTrap)) matches
                        tagged Exception .x: (zeroExtend(pack(x)));
                        tagged Interrupt .x: (zeroExtend(pack(x)) | 8'h80);
                    endcase) });

        if (maybeNextPc matches tagged Valid .replayPc) begin
            // This instruction didn't retire

            // redirect happens in Trap2
            nextPc <= replayPc;
            state <= Trap2;
        end else begin
            // This instruction retired
            // write to the register file
            rf.wr(toFullRegIndex(dInst.dst, getInstFields(inst).rd), fromMaybe(dataWb, maybeData));
            // always redirect
            pc <= nextPc;
            state <= IMMU;
        end
    endrule

    rule doTrap(state == Trap);
        // TODO: move this to WB
        let csrfResult <- csrf.wr(
                pc,
                tagged Invalid,
                getInstFields(inst).csr,
                0, // data
                addr,
                (exception matches tagged Valid .e ? tagged Valid (tagged Exception e) : tagged Invalid), // exception cause
                0,
                False,
                False);

        Maybe#(Addr) maybeNextPc = tagged Invalid;
        Maybe#(Data) maybeData = tagged Invalid;
        Maybe#(TrapCause) maybeTrap = tagged Invalid;
        case (csrfResult) matches
            tagged Exception .exc:
                begin
                    maybeNextPc = tagged Valid exc.trapHandlerPC;
                    maybeTrap = tagged Valid exc.exception;
                end
            tagged RedirectPC .newPc:
                maybeNextPc = tagged Valid newPc;
            tagged CsrData .data:
                maybeData = tagged Valid data;
            tagged None:
                noAction;
        endcase

        // send verification packet
        verificationPackets.enq( VerificationPacket {
                skippedPackets: 0,
                pc: pc,
                nextPc: fromMaybe(?, maybeNextPc),
                data: fromMaybe(data, maybeData),
                addr: addr,
                instruction: inst,
                dst: {pack(dInst.dst), getInstFields(inst).rd},
                trap: isValid(maybeTrap),
                trapType: (case (fromMaybe(unpack(0), maybeTrap)) matches
                        tagged Exception .x: (zeroExtend(pack(x)));
                        tagged Interrupt .x: (zeroExtend(pack(x)) | 8'h80);
                    endcase) });

        // redirection will happpen in trap2
        // by construction maybeNextPc is always valid
        nextPc <= fromMaybe(nextPc, maybeNextPc);
        state <= Trap2;
    endrule

    // There is a second trap state to ensure that the frontEndCsrs reflect the updated state of the processor
    rule doTrap2(state == Trap2);
        pc <= nextPc;
        state <= IMMU;
    endrule

    method Action start(Addr startPc);
        running <= True;
        pc <= startPc;
        state <= IMMU;
        csrState <= defaultValue;
        if (verbose) $fdisplay(fout, "[frontend] starting from pc = 0x%08x", startPc);
    endmethod
    method Action stop;
        running <= False;
        state <= Wait;
    endmethod

    method ActionValue#(VerificationPacket) getVerificationPacket;
        let verificationPacket = verificationPackets.first;
        verificationPackets.deq;
        return verificationPacket;
    endmethod

    method ActionValue#(VMInfo) updateVMInfoI;
        return csrf.vmI;
    endmethod
    method ActionValue#(VMInfo) updateVMInfoD;
        return csrf.vmD;
    endmethod

endmodule

// Copyright (c) 2016 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.

import ClientServer::*;
import Connectable::*;
import DefaultValue::*;
import FIFO::*;
import GetPut::*;
import Vector::*;

import Abstraction::*;
import RegUtil::*;
import RVRFile::*;
import RVCsrFile::*;
import RVExec::*;
import RVFpu::*;
import RVMulDiv::*;
import RVTypes::*;
import VerificationPacket::*;

import RVAlu::*;
import RVControl::*;
import RVDecode::*;
import RVMemory::*;

// This interface is the combination of FrontEnd and BackEnd
interface Core;
    method Action start(Addr startPc);
    method Action stop;

    method ActionValue#(VerificationPacket) getVerificationPacket;
    method ActionValue#(VMInfo) updateVMInfoI;
    method ActionValue#(VMInfo) updateVMInfoD;

    interface Client#(FenceReq, FenceResp) fence;
endinterface

instance Connectable#(Core, MemorySystem);
    module mkConnection#(Core core, MemorySystem mem)(Empty);
        mkConnection(core.fence, mem.fence);
        mkConnection(toGet(core.updateVMInfoI), toPut(mem.updateVMInfoI));
        mkConnection(toGet(core.updateVMInfoD), toPut(mem.updateVMInfoD));
    endmodule
endinstance

typedef enum {
    Wait,
    IMMU,
    IF,
    Dec,
    RegRead,
    Execute,
    Mem,
    WB,
    Trap,
    Trap2
} ProcState deriving (Bits, Eq, FShow);

module mkMulticycleCore#(
        Server#(RVIMMUReq, RVIMMUResp) ivat,
        Server#(RVIMemReq, RVIMemResp) ifetch,
        Server#(RVDMMUReq, RVDMMUResp) dvat,
        Server#(RVDMemReq, RVDMemResp) dmem,
        Bool ipi,
        Bool timerInterrupt,
        Data timer,
        Bool externalInterrupt,
        Data hartID
    )
        (Core);
    let verbose = False;
    File fout = stdout;

    ArchRFile rf <- mkArchRFile;
    RVCsrFile csrf <- mkRVCsrFile(hartID, timer, timerInterrupt, ipi, externalInterrupt);
    MulDivExec mulDiv <- mkBoothRoughMulDivExec;
    FpuExec fpu <- mkFpuExecPipeline;

    Reg#(Bool) running <- mkReg(False);
    Reg#(ProcState) state <- mkReg(Wait);

    Reg#(Addr) pc <- mkReg(0);
    Reg#(Maybe#(ExceptionCause)) exception <- mkReg(tagged Invalid);
    Reg#(Instruction) inst <- mkReg(0);
    Reg#(RVDecodedInst) dInst <- mkReg(unpack(0));
    Reg#(FrontEndCsrs) csrState <- mkReadOnlyReg( FrontEndCsrs { vmI: csrf.vmI, state: csrf.csrState } );

    Reg#(Data) rVal1 <- mkReg(0);
    Reg#(Data) rVal2 <- mkReg(0);
    Reg#(Data) rVal3 <- mkReg(0);
    Reg#(Data) data <- mkReg(0);
    Reg#(Data) addr <- mkReg(0);
    Reg#(Data) nextPc <- mkReg(0);

    FIFO#(Data)         toHost <- mkFIFO1;
    FIFO#(Data)         fromHost <- mkFIFO1;

    FIFO#(VerificationPacket) verificationPackets <- mkFIFO1;

    rule doInstMMU(running && state == IMMU);
        // request address translation from MMU
        ivat.request.put(pc);
        // reset states
        inst <= unpack(0);
        dInst <= unpack(0);
        exception <= tagged Invalid;
        // go to InstFetch stage
        state <= IF;
    endrule

    rule doInstFetch(state == IF);
        // I wanted notation like this:
        // let {addr: .phyPc, exception: .exMMU} = mmuResp.first;
        let resp <- ivat.response.get;
        let phyPc = resp.addr;
        let exMMU = resp.exception;

        if (!isValid(exMMU)) begin
            // no translation exception
            ifetch.request.put(phyPc);
            // go to decode stage
            state <= Dec;
        end else begin
            // translation exception (instruction access fault)
            exception <= exMMU;
            // send instruction to backend
            state <= Trap;
        end
    endrule

    rule doDecode(state == Dec);
        let fInst <- ifetch.response.get;

        let decInst = decodeInst(fInst);

        if (decInst matches tagged Valid .validDInst) begin
            // Legal instruction
            dInst <= validDInst;
        end else begin
            // Illegal instruction
            exception <= tagged Valid IllegalInst;
        end

        inst <= fInst;
        state <= isValid(decInst) ? RegRead : Trap;
    endrule

    rule doRegRead(state == RegRead);
        rVal1 <= rf.rd1(toFullRegIndex(dInst.rs1, getInstFields(inst).rs1));
        rVal2 <= rf.rd2(toFullRegIndex(dInst.rs2, getInstFields(inst).rs2));
        rVal3 <= rf.rd3(toFullRegIndex(dInst.rs3, getInstFields(inst).rs3));
        state <= Execute;
    endrule

    rule doExecute(state == Execute);
        let dataEx = 0;
        let addrEx = 0;
        let nextPcEx = pc + 4;

        Maybe#(Data) imm = getImmediate(dInst.imm, dInst.inst);
        case (dInst.execFunc) matches
            tagged Alu    .aluInst:
                begin
                    dataEx = execAluInst(aluInst, rVal1, rVal2, imm, pc);
                end
            tagged Br     .brFunc:
                begin
                    // data for jal
                    dataEx = pc + 4;
                    nextPcEx = execControl(brFunc, rVal1, rVal2, imm, pc);
                end
            tagged Mem    .memInst:
                begin
                    // data for store and AMO
                    dataEx = rVal2;
                    addrEx = addrCalc(rVal1, imm);
                    dvat.request.put(RVDMMUReq {addr: addrEx, size: memInst.size, op: (memInst.op matches tagged Mem .memOp ? memOp : St)});
                end
            tagged MulDiv .mulDivInst: mulDiv.exec(mulDivInst, rVal1, rVal2);
            tagged Fence  .fenceInst:  noAction;
            // TODO: Handle dynamic rounding mode
            tagged Fpu    .fpuInst:    fpu.exec(fpuInst, getInstFields(inst).rm, rVal1, rVal2, rVal3);
            tagged System .systemInst:
                begin
                    // data for CSR instructions
                    dataEx = fromMaybe(rVal1, imm);
                end
        endcase

        data <= dataEx;
        addr <= addrEx;
        nextPc <= nextPcEx;

        state <= dInst.execFunc matches tagged Mem .* ? Mem : WB;
    endrule

    rule doMem(state == Mem);
        let resp <- dvat.response.get;
        let pAddr = resp.addr;
        let exMMU = resp.exception;

        // TODO: make this type safe! get rid of .Mem accesses to tagged union
        if (!isValid(exMMU)) begin
            dmem.request.put( RVDMemReq {
                    op: dInst.execFunc.Mem.op,
                    size: dInst.execFunc.Mem.size,
                    isUnsigned: dInst.execFunc.Mem.isUnsigned,
                    addr: pAddr,
                    data: data } );
            state <= WB;
        end else begin
            exception <= exMMU;
            state <= Trap;
        end
    endrule

    rule doWB(state == WB);
        let dataWb = data;
        let addrWb = addr;
        let nextPcWb = nextPc;
        let fflagsWb = 0;
        let exceptionWB = exception;

        case(dInst.execFunc) matches
            tagged MulDiv .*: begin
                    dataWb = mulDiv.result_data();
                    mulDiv.result_deq;
                end
            tagged Fpu .*: begin
                    let fpuResult = toFullResult(fpu.result_data);
                    dataWb = fpuResult.data;
                    fflagsWb = fpuResult.fflags;
                    fpu.result_deq;
                end
            tagged Mem .memInst:
                begin
                    if (getsResponse(memInst.op)) begin
                        dataWb <- dmem.response.get;
                    end
                end
        endcase

        Maybe#(TrapCause) trap = tagged Invalid;
        if (exceptionWB matches tagged Valid .validException) begin
            trap = tagged Valid (tagged Exception validException);
        end

        if (dInst.execFunc matches tagged Mem .*) begin
            // don't bother checking for interrupts since you just did a memory operation
        end else begin
            // check for interrupts (this overwrites current exceptions)
            if (csrf.readyInterrupt matches tagged Valid .validInterrupt) begin
                trap = tagged Valid (tagged Interrupt validInterrupt);
            end
        end
        Bool checkForInterrupts = dInst.execFunc matches tagged Mem .* ? False : True;
        Bool extensionDirty = False;
        Bool fpuDirty = (dInst.dst == tagged Valid Fpu);
        let csrfResult <- csrf.wr(
                pc,
                // performing system instructions
                dInst.execFunc matches tagged System .sysInst ? tagged Valid sysInst : tagged Invalid,
                getInstFields(inst).csr,
                dataWb, // either rf[rs1] or zimm, computed in basicExec
                addrWb,
                // handling exceptions
                trap,
                // indirect writes
                fflagsWb,
                fpuDirty,
                extensionDirty);

        Maybe#(Addr) maybeNextPc = tagged Invalid;
        Maybe#(Data) maybeData = tagged Invalid;
        Maybe#(TrapCause) maybeTrap = tagged Invalid;
        case (csrfResult) matches
            tagged Exception .exc:
                begin
                    maybeNextPc = tagged Valid exc.trapHandlerPC;
                    maybeTrap = tagged Valid exc.exception;
                end
            tagged RedirectPC .newPc:
                maybeNextPc = tagged Valid newPc;
            tagged CsrData .data:
                maybeData = tagged Valid data;
            tagged None:
                noAction;
        endcase

        // send verification packet
        verificationPackets.enq( VerificationPacket {
                skippedPackets: 0,
                pc: pc,
                nextPc: fromMaybe(nextPcWb, maybeNextPc),
                data: fromMaybe(dataWb, maybeData),
                addr: addr,
                instruction: inst,
                dst: {pack(dInst.dst), getInstFields(inst).rd},
                trap: isValid(maybeTrap),
                trapType: (case (fromMaybe(unpack(0), maybeTrap)) matches
                        tagged Exception .x: (zeroExtend(pack(x)));
                        tagged Interrupt .x: (zeroExtend(pack(x)) | 8'h80);
                    endcase) });

        if (maybeNextPc matches tagged Valid .replayPc) begin
            // This instruction didn't retire

            // redirect happens in Trap2
            nextPc <= replayPc;
            state <= Trap2;
        end else begin
            // This instruction retired
            // write to the register file
            rf.wr(toFullRegIndex(dInst.dst, getInstFields(inst).rd), fromMaybe(dataWb, maybeData));
            // always redirect
            pc <= nextPc;
            state <= IMMU;
        end
    endrule

    rule doTrap(state == Trap);
        // TODO: move this to WB
        let csrfResult <- csrf.wr(
                pc,
                tagged Invalid,
                getInstFields(inst).csr,
                0, // data
                addr,
                (exception matches tagged Valid .e ? tagged Valid (tagged Exception e) : tagged Invalid), // exception cause
                0,
                False,
                False);

        Maybe#(Addr) maybeNextPc = tagged Invalid;
        Maybe#(Data) maybeData = tagged Invalid;
        Maybe#(TrapCause) maybeTrap = tagged Invalid;
        case (csrfResult) matches
            tagged Exception .exc:
                begin
                    maybeNextPc = tagged Valid exc.trapHandlerPC;
                    maybeTrap = tagged Valid exc.exception;
                end
            tagged RedirectPC .newPc:
                maybeNextPc = tagged Valid newPc;
            tagged CsrData .data:
                maybeData = tagged Valid data;
            tagged None:
                noAction;
        endcase

        // send verification packet
        verificationPackets.enq( VerificationPacket {
                skippedPackets: 0,
                pc: pc,
                nextPc: fromMaybe(?, maybeNextPc),
                data: fromMaybe(data, maybeData),
                addr: addr,
                instruction: inst,
                dst: {pack(dInst.dst), getInstFields(inst).rd},
                trap: isValid(maybeTrap),
                trapType: (case (fromMaybe(unpack(0), maybeTrap)) matches
                        tagged Exception .x: (zeroExtend(pack(x)));
                        tagged Interrupt .x: (zeroExtend(pack(x)) | 8'h80);
                    endcase) });

        // redirection will happpen in trap2
        // by construction maybeNextPc is always valid
        nextPc <= fromMaybe(nextPc, maybeNextPc);
        state <= Trap2;
    endrule

    // There is a second trap state to ensure that the frontEndCsrs reflect the updated state of the processor
    rule doTrap2(state == Trap2);
        pc <= nextPc;
        state <= IMMU;
    endrule

    method Action start(Addr startPc);
        running <= True;
        pc <= startPc;
        state <= IMMU;
        csrState <= defaultValue;
        if (verbose) $fdisplay(fout, "[frontend] starting from pc = 0x%08x", startPc);
    endmethod
    method Action stop;
        running <= False;
        state <= Wait;
    endmethod

    method ActionValue#(VerificationPacket) getVerificationPacket;
        let verificationPacket = verificationPackets.first;
        verificationPackets.deq;
        return verificationPacket;
    endmethod

    method ActionValue#(VMInfo) updateVMInfoI;
        return csrf.vmI;
    endmethod
    method ActionValue#(VMInfo) updateVMInfoD;
        return csrf.vmD;
    endmethod

endmodule