prep for merging interfaces HDMI/HDMIOut

// Copyright (c) 2013 Nokia, Inc.

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

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

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

import Vector::*;
import Clocks::*;
import FIFO::*;
import FIFOF::*;
import GetPut::*;

import YUV::*;
import NrccSyncBRAM::*;

interface HDMI;
    method Bit#(1) hdmi_vsync;
    method Bit#(1) hdmi_hsync;
    method Bit#(1) hdmi_de;
    method Bit#(16) hdmi_data;
    interface Clock hdmi_clock_if;
    interface Reset hdmi_reset_if;
endinterface

interface HdmiOut;
    interface Put#(Rgb888VideoData) rgb;
    interface HDMI hdmi;
endinterface

interface HdmiGenerator;
    method Action setTestPattern(Bool v);
    method Action setPatternColor(Bit#(32) v);
    method Action setHsyncWidth(Bit#(12) hsyncWidth);
    method Action setDePixelCountMinMax(Bit#(12) min, Bit#(12) max);
    method Action setVsyncWidth(Bit#(11) vsyncWidth);
    method Action setDeLineCountMinMax(Bit#(11) min, Bit#(11) max);
    method Action setNumberOfLines(Bit#(11) lines);
    method Action setNumberOfPixels(Bit#(12) pixels);
    interface HDMI hdmi;
endinterface

typedef struct {
    Bit#(11) line;
    Bit#(12) pixel;
} LinePixelCount;

typedef struct {
    Bit#(1) vsync;
    Bit#(1) hsync;
    Bool de;
    Rgb888 pixel;
    Bit#(12) dataCount;
} Rgb888Stage deriving (Bits);

typedef struct {
    Bit#(1) vsync;
    Bit#(1) hsync;
    Bool de;
    Yuv444Intermediates data;
} Yuv444IntermediatesStage deriving (Bits);

typedef struct {
    Bit#(1) vsync;
    Bit#(1) hsync;
    Bool de;
    Yuv444 data;
} Yuv444Stage deriving (Bits);

typedef struct {
    Bit#(1) vsync;
    Bit#(1) hsync;
    Bool de;
    Bit#(16) data;
} Yuv422Stage deriving (Bits);

module mkHdmiGenerator#(Clock axi_clock, Reset axi_reset,
                                   BRAM#(Bit#(12), Bit#(32)) lineBuffer,
                                   SyncPulseIfc vsyncPulse,
                                   SyncPulseIfc hsyncPulse)(HdmiGenerator);
    Clock defaultClock <- exposeCurrentClock();
    Reset defaultReset <- exposeCurrentReset();
    // 1920 * 1080
    Reg#(Bit#(12)) hsyncWidth <- mkSyncReg(44, axi_clock, axi_reset, defaultClock);
    Reg#(Bit#(12)) dePixelCountMinimum <- mkSyncReg(192, axi_clock, axi_reset, defaultClock);
    Reg#(Bit#(12)) dePixelCountMaximum <- mkSyncReg(2112, axi_clock, axi_reset, defaultClock);
    Reg#(Bit#(12)) pixelMidpoint <- mkSyncReg(1152, axi_clock, axi_reset, defaultClock);
    Reg#(Bit#(11)) vsyncWidth <- mkSyncReg(5, axi_clock, axi_reset, defaultClock);
    Reg#(Bit#(11)) deLineCountMinimum <- mkSyncReg(41, axi_clock, axi_reset, defaultClock);
    Reg#(Bit#(11)) deLineCountMaximum <- mkSyncReg(1121, axi_clock, axi_reset, defaultClock);
    Reg#(Bit#(11)) lineMidpoint <- mkSyncReg(581, axi_clock, axi_reset, defaultClock);

    Reg#(Bit#(11)) numberOfLines <- mkSyncReg(1125, axi_clock, axi_reset, defaultClock);
    Reg#(Bit#(12)) numberOfPixels <- mkSyncReg(2200, axi_clock, axi_reset, defaultClock);

    Reg#(Bit#(11)) lineCount <- mkReg(0);
    Reg#(Bit#(12)) pixelCount <- mkReg(0);

    FIFOF#(Rgb888Stage) bramOutStageFifo <- mkSizedFIFOF(8);

    Reg#(Bit#(12)) dataCount <- mkReg(0);
    Reg#(Bit#(32)) patternReg0 <- mkSyncReg(32'h00FFFFFF, axi_clock, axi_reset, defaultClock); // white

    Vector#(4, Reg#(Bit#(32))) patternRegs <- replicateM(mkReg(0));

    Reg#(Bool) shadowTestPatternEnabled <- mkSyncReg(True, axi_clock, axi_reset, defaultClock);
    Reg#(Bool) testPatternEnabled <- mkReg(True);

    let vsync = (lineCount < vsyncWidth) ? 1 : 0;
    let hsync = (pixelCount < hsyncWidth) ? 1 : 0;

    let isActiveLine = (lineCount >= deLineCountMinimum && lineCount < deLineCountMaximum);
    let dataEnable = (pixelCount >= dePixelCountMinimum && pixelCount < dePixelCountMaximum
                      && lineCount >= deLineCountMinimum && lineCount < deLineCountMaximum);

    function LinePixelCount newCounts(Bit#(11) lc, Bit#(12) pc);
        let newLineCount = lc;
        let newPixelCount = pc;
        if (pc == numberOfPixels-1)
        begin
           newPixelCount = 0;
           if (lc == numberOfLines-1)
           begin
               newLineCount = 0;
           end
           else
               newLineCount = lc+1;
        end
        else
        begin
            newPixelCount = pc + 1;
        end
        return LinePixelCount { line: newLineCount, pixel: newPixelCount };
    endfunction

    // vsyncPulse is a SyncHandshake to a slow clock domain
    // so it is not ready every cycle.
    // Therefore, we send to it from a different rule so it does not block the fbRule
    rule sendVsyncPulse (lineCount == 0 && pixelCount == 0);
        $display("vsync pulse sent");
        vsyncPulse.send();
    endrule

    rule sendHsyncPulse (isActiveLine && pixelCount == 0);
        //$display("hsync pulse sent");
        hsyncPulse.send();
    endrule

    rule data if (testPatternEnabled);
        LinePixelCount counts = newCounts(lineCount, pixelCount);
        lineCount <= counts.line;
        pixelCount <= counts.pixel;

        //if (pixelCount == 0) $display("tpg line %d", lineCount);

        if (lineCount == 0 && pixelCount == 0)
        begin
            $display("testPatternEnabled %d", shadowTestPatternEnabled);
            testPatternEnabled <= shadowTestPatternEnabled;

            patternRegs[0] <= patternReg0;
            patternRegs[1] <= 32'h00FF0000; // blue
            patternRegs[2] <= 32'h0000FF00; // green
            patternRegs[3] <= 32'h000000FF; // red
            //patternRegs[1] <= 32'h80ff80ff; // yuv422 white
            //patternRegs[2] <= 32'h2c961596; // yuv422 green
            //patternRegs[3] <= 32'hff1d6b1d; // yuv422 red
        end

        Bit#(2) index = 0;
        if (pixelCount >= pixelMidpoint)
            index[0] = 1;
        if (lineCount >= lineMidpoint)
            index[1] = 1;
        Bit#(32) data = patternRegs[index];

        bramOutStageFifo.enq(Rgb888Stage { vsync: vsync, hsync: hsync, de: dataEnable, pixel: unpack(truncate(data)), dataCount: 0 });
        lineBuffer.readAddr(0);

        if (dataEnable)
            dataCount <= dataCount + 1;
        else if (hsync == 1)
            dataCount <= 0;

    endrule

    rule fbRule if (!testPatternEnabled);
        LinePixelCount counts = newCounts(lineCount, pixelCount);
        lineCount <= counts.line;
        pixelCount <= counts.pixel;

        //if (pixelCount == 0) $display("fb line %d", lineCount);

        if (lineCount == 0 && pixelCount == 0)
        begin
            $display("testPatternEnabled %d", shadowTestPatternEnabled);
            testPatternEnabled <= shadowTestPatternEnabled;
        end

        if (hsync == 1)
            dataCount <= 0;
        else if (dataEnable)
            dataCount <= dataCount + 1;

        bramOutStageFifo.enq(Rgb888Stage { vsync: vsync, hsync: hsync, de: dataEnable, pixel: Rgb888{r:0,g:0,b:0}, dataCount: dataCount });
        lineBuffer.readAddr(dataCount);

    endrule

    Reg#(Rgb888Stage) rgb888StageReg <- mkReg(unpack(0));
    Reg#(Yuv444IntermediatesStage) yuv444IntermediatesStageReg <- mkReg(
        Yuv444IntermediatesStage { vsync: 0, hsync: 0, de: False, data: unpack(0) });
    Reg#(Yuv444Stage) yuv444StageReg <- mkReg(
        Yuv444Stage { vsync: 0, hsync: 0, de: False, data: unpack(0) });
    Reg#(Yuv422Stage) yuv422StageReg <- mkReg(
        Yuv422Stage { vsync: 0, hsync: 0, de: False, data: unpack(0) });
    Reg#(Bool) evenOddPixelReg <- mkReg(False);

    rule bramOutStage;
        let d <- lineBuffer.readData;
        let stageData = bramOutStageFifo.first;
        bramOutStageFifo.deq;

        if (!testPatternEnabled)
        begin
            let pixel = stageData.pixel;
            //let pixelSelect = 0; //stageData.dataCount[0];
            //if (pixelSelect == 0)
                pixel = unpack(d[23:0]);
            //else
                //pixel = unpack(d[55:32]);
            stageData.pixel = pixel;
        end
        rgb888StageReg <= stageData;
    endrule

    rule yuv444IntermediatesStage;
        let previous = rgb888StageReg;
        let pixel = previous.pixel;
        yuv444IntermediatesStageReg <= Yuv444IntermediatesStage {
            vsync: previous.vsync,
            hsync: previous.hsync,
            de: previous.de,
            data: (previous.de) ? rgbToYuvIntermediates(pixel) : unpack(0)
        };
    endrule

    rule yuv444Stage;
        let previous = yuv444IntermediatesStageReg;
        yuv444StageReg <= Yuv444Stage {
            vsync: previous.vsync,
            hsync: previous.hsync,
            de: previous.de,
            data: (previous.de) ? yuvIntermediatesToYuv444(previous.data) : unpack(0)
        };
    endrule

    rule yuv422stage;
        let previous = yuv444StageReg;
        if (previous.de)
            evenOddPixelReg <= !evenOddPixelReg;
        Bit#(16) data = { evenOddPixelReg ? previous.data.u : previous.data.v,
                          previous.data.y };
        yuv422StageReg <= Yuv422Stage {
            vsync: previous.vsync,
            hsync: previous.hsync,
            de: previous.de,
            data: data
        };
    endrule

    interface HDMI hdmi;
        method Bit#(1) hdmi_vsync;
            return yuv422StageReg.vsync;
        endmethod
        method Bit#(1) hdmi_hsync;
            return yuv422StageReg.hsync;
        endmethod
        method Bit#(1) hdmi_de;
            return yuv422StageReg.de ? 1 : 0;
        endmethod
        method Bit#(16) hdmi_data;
            return yuv422StageReg.data;
        endmethod
        interface hdmi_clock_if = defaultClock;
        interface hdmi_reset_if = defaultReset;
    endinterface

    method Action setPatternColor(Bit#(32) v);
        patternReg0 <= v;
    endmethod
    method Action setTestPattern(Bool v);
        shadowTestPatternEnabled <= v;
    endmethod
    method Action setHsyncWidth(Bit#(12) width);
        hsyncWidth <= width;
    endmethod
    method Action setDePixelCountMinMax(Bit#(12) min, Bit#(12) max);
        dePixelCountMinimum <= min;
        dePixelCountMaximum <= max;
        pixelMidpoint <= (min + max) / 2;
    endmethod
    method Action setVsyncWidth(Bit#(11) width);
        vsyncWidth <= width;
    endmethod
    method Action setDeLineCountMinMax(Bit#(11) min, Bit#(11) max);
        deLineCountMinimum <= min;
        deLineCountMaximum <= max;
        lineMidpoint <= (min + max) / 2;
    endmethod
    method Action setNumberOfLines(Bit#(11) lines);
        numberOfLines <= lines;
    endmethod
    method Action setNumberOfPixels(Bit#(12) pixels);
        numberOfPixels <= pixels;
    endmethod
endmodule

module mkHdmiOut(HdmiOut);
    Clock defaultClock <- exposeCurrentClock();
    Reset defaultReset <- exposeCurrentReset();
    Reg#(Rgb888Stage) rgb888StageReg <- mkReg(unpack(0));
    Reg#(Yuv444IntermediatesStage) yuv444IntermediatesStageReg <- mkReg(
        Yuv444IntermediatesStage { vsync: 0, hsync: 0, de: False, data: unpack(0) });
    Reg#(Yuv444Stage) yuv444StageReg <- mkReg(
        Yuv444Stage { vsync: 0, hsync: 0, de: False, data: unpack(0) });
    Reg#(Yuv422Stage) yuv422StageReg <- mkReg(
        Yuv422Stage { vsync: 0, hsync: 0, de: False, data: unpack(0) });
    Reg#(Bool) evenOddPixelReg <- mkReg(False);

    rule yuv444IntermediatesStage;
        let previous = rgb888StageReg;
        let pixel = previous.pixel;
        yuv444IntermediatesStageReg <= Yuv444IntermediatesStage {
            vsync: previous.vsync,
            hsync: previous.hsync,
            de: previous.de,
            data: (previous.de) ? rgbToYuvIntermediates(pixel) : unpack(0)
        };
    endrule

    rule yuv444Stage;
        let previous = yuv444IntermediatesStageReg;
        yuv444StageReg <= Yuv444Stage {
            vsync: previous.vsync,
            hsync: previous.hsync,
            de: previous.de,
            data: (previous.de) ? yuvIntermediatesToYuv444(previous.data) : unpack(0)
        };
    endrule

    rule yuv422stage;
        let previous = yuv444StageReg;
        if (previous.de)
            evenOddPixelReg <= !evenOddPixelReg;
        Bit#(16) data = { evenOddPixelReg ? previous.data.u : previous.data.v,
                          previous.data.y };
        yuv422StageReg <= Yuv422Stage {
            vsync: previous.vsync,
            hsync: previous.hsync,
            de: previous.de,
            data: data
        };
    endrule

    interface Put rgb;
        method Action put(Rgb888VideoData videoData);
            rgb888StageReg <= Rgb888Stage {
                vsync: videoData.vsync,
                hsync: videoData.hsync,
                de: videoData.active_video == 1 ? True : False,
                pixel: Rgb888 { r: videoData.r, g: videoData.g, b: videoData.b },
		dataCount: 0
            };
        endmethod
    endinterface
    interface HDMI hdmi;
        method Bit#(1) hdmi_vsync;
            return yuv422StageReg.vsync;
        endmethod
        method Bit#(1) hdmi_hsync;
            return yuv422StageReg.hsync;
        endmethod
        method Bit#(1) hdmi_de;
            return yuv422StageReg.de ? 1 : 0;
        endmethod
        method Bit#(16) hdmi_data;
            return yuv422StageReg.data;
        endmethod
        interface hdmi_clock_if = defaultClock;
        interface hdmi_reset_if = defaultReset;
    endinterface
endmodule

// Copyright (c) 2013 Nokia, Inc.

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

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

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

import Vector::*;
import Clocks::*;
import FIFO::*;
import FIFOF::*;
import GetPut::*;

import YUV::*;
import NrccSyncBRAM::*;

typedef struct {
    Bit#(1) vsync;
    Bit#(1) hsync;
    Bit#(1) de;
    Bit#(16) data;
} HdmiData deriving (Bits);

interface HDMI;
    method Bit#(1) hdmi_vsync;
    method Bit#(1) hdmi_hsync;
    method Bit#(1) hdmi_de;
    method Bit#(16) hdmi_data;
    interface Clock hdmi_clock_if;
    interface Reset hdmi_reset_if;
endinterface

interface HdmiOut;
    interface Put#(Rgb888VideoData) rgb;
    interface HDMI hdmi;
endinterface

interface HdmiGenerator;
    method Action setTestPattern(Bool v);
    method Action setPatternColor(Bit#(32) v);
    method Action setHsyncWidth(Bit#(12) hsyncWidth);
    method Action setDePixelCountMinMax(Bit#(12) min, Bit#(12) max);
    method Action setVsyncWidth(Bit#(11) vsyncWidth);
    method Action setDeLineCountMinMax(Bit#(11) min, Bit#(11) max);
    method Action setNumberOfLines(Bit#(11) lines);
    method Action setNumberOfPixels(Bit#(12) pixels);
//
    //method Bool vsync();
    //method Bool hsync();
    interface HDMI hdmi;
endinterface

typedef struct {
    Bit#(11) line;
    Bit#(12) pixel;
} LinePixelCount;

typedef struct {
    Bit#(1) vsync;
    Bit#(1) hsync;
    Bool de;
    Rgb888 pixel;
    Bit#(12) dataCount;
} Rgb888Stage deriving (Bits);

typedef struct {
    Bit#(1) vsync;
    Bit#(1) hsync;
    Bool de;
    Yuv444Intermediates data;
} Yuv444IntermediatesStage deriving (Bits);

typedef struct {
    Bit#(1) vsync;
    Bit#(1) hsync;
    Bool de;
    Yuv444 data;
} Yuv444Stage deriving (Bits);

typedef struct {
    Bit#(1) vsync;
    Bit#(1) hsync;
    Bool de;
    Bit#(16) data;
} Yuv422Stage deriving (Bits);

module mkHdmiGenerator#(Clock axi_clock, Reset axi_reset,
                                   BRAM#(Bit#(12), Bit#(32)) lineBuffer,
                                   SyncPulseIfc vsyncPulse,
                                   SyncPulseIfc hsyncPulse)(HdmiGenerator);
    Clock defaultClock <- exposeCurrentClock();
    Reset defaultReset <- exposeCurrentReset();
    // 1920 * 1080
    Reg#(Bit#(12)) hsyncWidth <- mkSyncReg(44, axi_clock, axi_reset, defaultClock);
    Reg#(Bit#(12)) dePixelCountMinimum <- mkSyncReg(192, axi_clock, axi_reset, defaultClock);
    Reg#(Bit#(12)) dePixelCountMaximum <- mkSyncReg(2112, axi_clock, axi_reset, defaultClock);
    Reg#(Bit#(12)) pixelMidpoint <- mkSyncReg(1152, axi_clock, axi_reset, defaultClock);
    Reg#(Bit#(11)) vsyncWidth <- mkSyncReg(5, axi_clock, axi_reset, defaultClock);
    Reg#(Bit#(11)) deLineCountMinimum <- mkSyncReg(41, axi_clock, axi_reset, defaultClock);
    Reg#(Bit#(11)) deLineCountMaximum <- mkSyncReg(1121, axi_clock, axi_reset, defaultClock);
    Reg#(Bit#(11)) lineMidpoint <- mkSyncReg(581, axi_clock, axi_reset, defaultClock);

    Reg#(Bit#(11)) numberOfLines <- mkSyncReg(1125, axi_clock, axi_reset, defaultClock);
    Reg#(Bit#(12)) numberOfPixels <- mkSyncReg(2200, axi_clock, axi_reset, defaultClock);

    Reg#(Bit#(11)) lineCount <- mkReg(0);
    Reg#(Bit#(12)) pixelCount <- mkReg(0);

    FIFOF#(Rgb888Stage) bramOutStageFifo <- mkSizedFIFOF(8);
    Reg#(Rgb888Stage) rgb888StageReg <- mkReg(unpack(0));
    Reg#(Yuv444IntermediatesStage) yuv444IntermediatesStageReg <- mkReg(Yuv444IntermediatesStage { vsync: 0, hsync: 0, de: False, data: unpack(0) });
    Reg#(Yuv444Stage) yuv444StageReg <- mkReg(Yuv444Stage { vsync: 0, hsync: 0, de: False, data: unpack(0) });
    Reg#(Yuv422Stage) yuv422StageReg <- mkReg(Yuv422Stage { vsync: 0, hsync: 0, de: False, data: unpack(0) });
    Reg#(Bool) evenOddPixelReg <- mkReg(False);

    Reg#(Bit#(12)) dataCount <- mkReg(0);
    Reg#(Bit#(32)) patternReg0 <- mkSyncReg(32'h00FFFFFF, axi_clock, axi_reset, defaultClock); // white

    Vector#(4, Reg#(Bit#(32))) patternRegs <- replicateM(mkReg(0));

    Reg#(Bool) shadowTestPatternEnabled <- mkSyncReg(True, axi_clock, axi_reset, defaultClock);
    Reg#(Bool) testPatternEnabled <- mkReg(True);

    let vsync = (lineCount < vsyncWidth) ? 1 : 0;
    let hsync = (pixelCount < hsyncWidth) ? 1 : 0;

    let isActiveLine = (lineCount >= deLineCountMinimum && lineCount < deLineCountMaximum);
    let dataEnable = (pixelCount >= dePixelCountMinimum && pixelCount < dePixelCountMaximum
                      && lineCount >= deLineCountMinimum && lineCount < deLineCountMaximum);

    function LinePixelCount newCounts(Bit#(11) lc, Bit#(12) pc);
        let newLineCount = lc;
        let newPixelCount = pc;
        if (pc == numberOfPixels-1)
        begin
           newPixelCount = 0;
           if (lc == numberOfLines-1)
           begin
               newLineCount = 0;
           end
           else
               newLineCount = lc+1;
        end
        else
        begin
            newPixelCount = pc + 1;
        end
        return LinePixelCount { line: newLineCount, pixel: newPixelCount };
    endfunction

    // vsyncPulse is a SyncHandshake to a slow clock domain
    // so it is not ready every cycle.
    // Therefore, we send to it from a different rule so it does not block the fbRule
    rule sendVsyncPulse (lineCount == 0 && pixelCount == 0);
        $display("vsync pulse sent");
        vsyncPulse.send();
    endrule

    rule sendHsyncPulse (isActiveLine && pixelCount == 0);
        //$display("hsync pulse sent");
        hsyncPulse.send();
    endrule

    rule data if (testPatternEnabled);
        LinePixelCount counts = newCounts(lineCount, pixelCount);
        lineCount <= counts.line;
        pixelCount <= counts.pixel;

        //if (pixelCount == 0) $display("tpg line %d", lineCount);

        if (lineCount == 0 && pixelCount == 0)
        begin
            $display("testPatternEnabled %d", shadowTestPatternEnabled);
            testPatternEnabled <= shadowTestPatternEnabled;

            patternRegs[0] <= patternReg0;
            patternRegs[1] <= 32'h00FF0000; // blue
            patternRegs[2] <= 32'h0000FF00; // green
            patternRegs[3] <= 32'h000000FF; // red
            //patternRegs[1] <= 32'h80ff80ff; // yuv422 white
            //patternRegs[2] <= 32'h2c961596; // yuv422 green
            //patternRegs[3] <= 32'hff1d6b1d; // yuv422 red
        end

        Bit#(2) index = 0;
        if (pixelCount >= pixelMidpoint)
            index[0] = 1;
        if (lineCount >= lineMidpoint)
            index[1] = 1;
        Bit#(32) data = patternRegs[index];

        bramOutStageFifo.enq(Rgb888Stage { vsync: vsync, hsync: hsync, de: dataEnable, pixel: unpack(truncate(data)) });
        lineBuffer.readAddr(0);

        if (dataEnable)
            dataCount <= dataCount + 1;
        else if (hsync == 1)
            dataCount <= 0;

    endrule

    rule fbRule if (!testPatternEnabled);
        LinePixelCount counts = newCounts(lineCount, pixelCount);
        lineCount <= counts.line;
        pixelCount <= counts.pixel;

        //if (pixelCount == 0) $display("fb line %d", lineCount);

        if (lineCount == 0 && pixelCount == 0)
        begin
            $display("testPatternEnabled %d", shadowTestPatternEnabled);
            testPatternEnabled <= shadowTestPatternEnabled;
        end

        if (hsync == 1)
            dataCount <= 0;
        else if (dataEnable)
            dataCount <= dataCount + 1;

        bramOutStageFifo.enq(Rgb888Stage { vsync: vsync, hsync: hsync, de: dataEnable, dataCount: dataCount });
        lineBuffer.readAddr(dataCount);

    endrule

    rule bramOutStage;
        let d <- lineBuffer.readData;
        let stageData = bramOutStageFifo.first;
        bramOutStageFifo.deq;

        if (!testPatternEnabled)
        begin
            let pixel = stageData.pixel;
            let pixelSelect = 0; //stageData.dataCount[0];
            if (pixelSelect == 0)
                pixel = unpack(d[23:0]);
            else
                pixel = unpack(d[55:32]);
            stageData.pixel = pixel;
        end
        rgb888StageReg <= stageData;
    endrule

    rule yuv444IntermediatesStage;
        let previous = rgb888StageReg;
        let pixel = previous.pixel;
        yuv444IntermediatesStageReg <= Yuv444IntermediatesStage {
            vsync: previous.vsync,
            hsync: previous.hsync,
            de: previous.de,
            data: (previous.de) ? rgbToYuvIntermediates(pixel) : unpack(0)
        };
    endrule

    rule yuv444Stage;
        let previous = yuv444IntermediatesStageReg;
        yuv444StageReg <= Yuv444Stage {
            vsync: previous.vsync,
            hsync: previous.hsync,
            de: previous.de,
            data: (previous.de) ? yuvIntermediatesToYuv444(previous.data) : unpack(0)
        };
    endrule

    rule yuv422stage;
        let previous = yuv444StageReg;
        if (previous.de)
            evenOddPixelReg <= !evenOddPixelReg;
        Bit#(16) data = { evenOddPixelReg ? previous.data.u : previous.data.v,
                          previous.data.y };
        yuv422StageReg <= Yuv422Stage {
            vsync: previous.vsync,
            hsync: previous.hsync,
            de: previous.de,
            data: data
        };
    endrule

    method Action setPatternColor(Bit#(32) v);
        patternReg0 <= v;
    endmethod
    method Action setTestPattern(Bool v);
        shadowTestPatternEnabled <= v;
    endmethod
    method Action setHsyncWidth(Bit#(12) width);
        hsyncWidth <= width;
    endmethod
    method Action setDePixelCountMinMax(Bit#(12) min, Bit#(12) max);
        dePixelCountMinimum <= min;
        dePixelCountMaximum <= max;
        pixelMidpoint <= (min + max) / 2;
    endmethod
    method Action setVsyncWidth(Bit#(11) width);
        vsyncWidth <= width;
    endmethod
    method Action setDeLineCountMinMax(Bit#(11) min, Bit#(11) max);
        deLineCountMinimum <= min;
        deLineCountMaximum <= max;
        lineMidpoint <= (min + max) / 2;
    endmethod
    method Action setNumberOfLines(Bit#(11) lines);
        numberOfLines <= lines;
    endmethod
    method Action setNumberOfPixels(Bit#(12) pixels);
        numberOfPixels <= pixels;
    endmethod

    interface HDMI hdmi;
        method Bit#(1) hdmi_vsync;
            return yuv422StageReg.vsync;
        endmethod
        method Bit#(1) hdmi_hsync;
            return yuv422StageReg.hsync;
        endmethod
        method Bit#(1) hdmi_de;
            return yuv422StageReg.de ? 1 : 0;
        endmethod
        method Bit#(16) hdmi_data;
            return yuv422StageReg.data;
        endmethod
        interface hdmi_clock_if = defaultClock;
        interface hdmi_reset_if = defaultReset;
    endinterface

endmodule

module mkHdmiOut(HdmiOut);

    Clock defaultClock <- exposeCurrentClock();
    Reset defaultReset <- exposeCurrentReset();
    Wire#(Rgb888Stage) rgb888StageWire <- mkDWire(unpack(0));
    Reg#(Yuv444IntermediatesStage) yuv444IntermediatesStageReg <- mkReg(Yuv444IntermediatesStage { vsync: 0, hsync: 0, de: False, data: unpack(0) });
    Reg#(Yuv444Stage) yuv444StageReg <- mkReg(Yuv444Stage { vsync: 0, hsync: 0, de: False, data: unpack(0) });
    Reg#(Yuv422Stage) yuv422StageReg <- mkReg(Yuv422Stage { vsync: 0, hsync: 0, de: False, data: unpack(0) });
    Reg#(Bool) evenOddPixelReg <- mkReg(False);

    rule yuv444IntermediatesStage;
        let previous = rgb888StageWire;
        let pixel = previous.pixel;
        yuv444IntermediatesStageReg <= Yuv444IntermediatesStage {
            vsync: previous.vsync,
            hsync: previous.hsync,
            de: previous.de,
            data: (previous.de) ? rgbToYuvIntermediates(pixel) : unpack(0)
        };
    endrule

    rule yuv444Stage;
        let previous = yuv444IntermediatesStageReg;
        yuv444StageReg <= Yuv444Stage {
            vsync: previous.vsync,
            hsync: previous.hsync,
            de: previous.de,
            data: (previous.de) ? yuvIntermediatesToYuv444(previous.data) : unpack(0)
        };
    endrule

    rule yuv422stage;
        let previous = yuv444StageReg;
        if (previous.de)
            evenOddPixelReg <= !evenOddPixelReg;
        Bit#(16) data = { evenOddPixelReg ? previous.data.u : previous.data.v,
                          previous.data.y };
        yuv422StageReg <= Yuv422Stage {
            vsync: previous.vsync,
            hsync: previous.hsync,
            de: previous.de,
            data: data
        };
    endrule

    interface Put rgb;
        method Action put(Rgb888VideoData videoData);
            rgb888StageWire <= Rgb888Stage {
                vsync: videoData.vsync,
                hsync: videoData.hsync,
                de: videoData.active_video == 1 ? True : False,
                pixel: Rgb888 { r: videoData.r, g: videoData.g, b: videoData.b },
		dataCount: 0
            };
        endmethod
    endinterface
    interface HDMI hdmi;
        method Bit#(1) hdmi_vsync;
            return yuv422StageReg.vsync;
        endmethod
        method Bit#(1) hdmi_hsync;
            return yuv422StageReg.hsync;
        endmethod
        method Bit#(1) hdmi_de;
            return yuv422StageReg.de ? 1 : 0;
        endmethod
        method Bit#(16) hdmi_data;
            return yuv422StageReg.data;
        endmethod
        interface hdmi_clock_if = defaultClock;
        interface hdmi_reset_if = defaultReset;
    endinterface
endmodule


Multiple format DCT{4,8,16,32}

Multiple format DCT{4,8,16,32}

// Copyright (c) 2016 Min Chen
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//

import Vector::*;
import GetPut::*;
import Connectable::*;
import ConfigReg::*;
import FIFOF::*;
import SpecialFIFOs::*;
import BUtils::*;
import BRAMCore::*;
import SMul::*;
import ARAM::*;
import Utils::*;

// Internal ROM Table
Integer g_t32[32][16] =
{
    { 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 },
    { 90, 90, 88, 85, 82, 78, 73, 67, 61, 54, 46, 38, 31, 22, 13,  4 },
    { 90, 87, 80, 70, 57, 43, 25,  9, -9,-25,-43,-57,-70,-80,-87,-90 },
    { 90, 82, 67, 46, 22, -4,-31,-54,-73,-85,-90,-88,-78,-61,-38,-13 },
    { 89, 75, 50, 18,-18,-50,-75,-89,-89,-75,-50,-18, 18, 50, 75, 89 },
    { 88, 67, 31,-13,-54,-82,-90,-78,-46, -4, 38, 73, 90, 85, 61, 22 },
    { 87, 57,  9,-43,-80,-90,-70,-25, 25, 70, 90, 80, 43, -9,-57,-87 },
    { 85, 46,-13,-67,-90,-73,-22, 38, 82, 88, 54, -4,-61,-90,-78,-31 },
    { 83, 36,-36,-83,-83,-36, 36, 83, 83, 36,-36,-83,-83,-36, 36, 83 },
    { 82, 22,-54,-90,-61, 13, 78, 85, 31,-46,-90,-67,  4, 73, 88, 38 },
    { 80,  9,-70,-87,-25, 57, 90, 43,-43,-90,-57, 25, 87, 70, -9,-80 },
    { 78, -4,-82,-73, 13, 85, 67,-22,-88,-61, 31, 90, 54,-38,-90,-46 },
    { 75,-18,-89,-50, 50, 89, 18,-75,-75, 18, 89, 50,-50,-89,-18, 75 },
    { 73,-31,-90,-22, 78, 67,-38,-90,-13, 82, 61,-46,-88, -4, 85, 54 },
    { 70,-43,-87,  9, 90, 25,-80,-57, 57, 80,-25,-90, -9, 87, 43,-70 },
    { 67,-54,-78, 38, 85,-22,-90,  4, 90, 13,-88,-31, 82, 46,-73,-61 },
    { 64,-64,-64, 64, 64,-64,-64, 64, 64,-64,-64, 64, 64,-64,-64, 64 },
    { 61,-73,-46, 82, 31,-88,-13, 90, -4,-90, 22, 85,-38,-78, 54, 67 },
    { 57,-80,-25, 90, -9,-87, 43, 70,-70,-43, 87,  9,-90, 25, 80,-57 },
    { 54,-85, -4, 88,-46,-61, 82, 13,-90, 38, 67,-78,-22, 90,-31,-73 },
    { 50,-89, 18, 75,-75,-18, 89,-50,-50, 89,-18,-75, 75, 18,-89, 50 },
    { 46,-90, 38, 54,-90, 31, 61,-88, 22, 67,-85, 13, 73,-82,  4, 78 },
    { 43,-90, 57, 25,-87, 70,  9,-80, 80, -9,-70, 87,-25,-57, 90,-43 },
    { 38,-88, 73, -4,-67, 90,-46,-31, 85,-78, 13, 61,-90, 54, 22,-82 },
    { 36,-83, 83,-36,-36, 83,-83, 36, 36,-83, 83,-36,-36, 83,-83, 36 },
    { 31,-78, 90,-61,  4, 54,-88, 82,-38,-22, 73,-90, 67,-13,-46, 85 },
    { 25,-70, 90,-80, 43,  9,-57, 87,-87, 57, -9,-43, 80,-90, 70,-25 },
    { 22,-61, 85,-90, 73,-38, -4, 46,-78, 90,-82, 54,-13,-31, 67,-88 },
    { 18,-50, 75,-89, 89,-75, 50,-18,-18, 50,-75, 89,-89, 75,-50, 18 },
    { 13,-38, 61,-78, 88,-90, 85,-73, 54,-31,  4, 22,-46, 67,-82, 90 },
    {  9,-25, 43,-57, 70,-80, 87,-90, 90,-87, 80,-70, 57,-43, 25, -9 },
    {  4,-13, 22,-31, 38,-46, 54,-61, 67,-73, 78,-82, 85,-88, 90,-90 }
};

// External interface
interface IDct32;
   method Action setSize(Bit#(2) dsize);
   interface Put#(Vector#(2, Vector#(32, Bit#(9)))) io_in;
   interface Get#(Vector#(32, Bit#(16))) io_out;
endinterface

interface IDct32Core#(numeric type a);
   method Vector#(15, Bit#(16)) dct(Vector#(16, Bit#(a)) x, Bit#(5) idx);
endinterface

module mkDct32Core(IDct32Core#(a))
   provisos(
      Add#(0, 8, b),
      Add#(1, c, TAdd#(a, b)),
      Add#(c, 1, c1),
      Add#(c, 2, c2),
      Add#(a, 10/*(5-1+6)*/, bs),
      Add#(shift32, 16, bs),
      Add#(1, shift16, shift32),
      Add#(2, shift8, shift32),
      Add#(3, shift4, shift32),
      Add#(0, TExp#(TSub#(shift32, 1)), round),
      Add#(_x1, 9, a),
      Add#(_x2, 16, c),
      Add#(_x3, 16, c1),
      Add#(_x4, 16, c2)
   );

   Vector#(16, SMUL#(a,b)) smul <- replicateM(mkSMUL);

   method Vector#(15, Bit#(16)) dct(Vector#(16, Bit#(a)) x, Bit#(5) idx);
      Bit#(c) x0[16];
      for(Integer i = 0; i < 16; i = i + 1)
         x0[i] = smul[i].c(x[i], fromInteger(g_t32[idx][i]));

      // Export 4 of DCT4 result from here
      Bit#(c1) x1[8];
      for(Integer i = 0; i < 8; i = i + 1)
         x1[i] = sExtend(x0[2 * i + 0]) + sExtend(x0[2 * i + 1]);

      // Export DCT8 result from here
      Bit#(c2) x2[4];
      for(Integer i = 0; i < 4; i = i + 1)
         x2[i] = sExtend(x1[2 * i + 0]) + sExtend(x1[2 * i + 1]);

      // Export DCT16 result from here
      Bit#(bs) x3[2];
      for(Integer i = 0; i < 2; i = i + 1)
         x3[i] = sExtend(x2[2 * i + 0]) + sExtend(x2[2 * i + 1]);

      // Export DCT32 result from here
      Bit#(bs) x4 = sExtend(x3[0]) + sExtend(x3[1]);

      // Generate DCT{4,8,16,32} coeff
      Vector#(15, Bit#(16)) coeff;

      for(Integer i = 0; i < 8; i = i + 1)
         coeff[0 + i] = roundN(x0[i], valueOf(shift4));

      for(Integer i = 0; i < 4; i = i + 1)
         coeff[8 + i] = roundN(x1[i], valueOf(shift8));

      for(Integer i = 0; i < 2; i = i + 1)
         coeff[12 + i] = roundN(x2[i], valueOf(shift16));

      coeff[14] = roundN(x4, valueOf(shift32));

      return coeff;
   endmethod
endmodule

(* synthesize *)
module mkDct32(IDct32);
   FIFOF#(Vector#(2, Vector#(32, Bit#(10)))) fifo_in     <- mkPipelineFIFOF;         // [E[0..15] O[0..15]]
   FIFOF#(Vector#(32, Bit#(16)))             fifo_out    <- mkPipelineFIFOF;
   Reg#(Bit#(2))                             dct_size    <- mkReg(3);

   Reg#(Bit#(16)) cycles <- mkReg(0);
   rule do_cycles;
      cycles <= cycles + 1;
   endrule

   // Internal
   Vector#(16, BRAM_SDP_PORT#(5,64,6,32))  mem <- replicateM(mkRAM_Nx1);

   Reg#(Bit#(9)) rowIdx <- mkConfigReg(0);
   Reg#(Bit#(6)) colCnt <- mkConfigReg(0);
   Reg#(Bit#(3)) colIdx <- mkConfigReg(0);

   Vector#(4, IDct32Core#(10)) dct_1D <- replicateM(mkDct32Core);
   Vector#(4, IDct32Core#(17)) dct_2D <- replicateM(mkDct32Core);
   Wire#(Bit#(1)) w_syncCol <- mkDWire(0);
   Reg#(Bit#(2)) syncCol <- mkReg(0);
   FIFOF#(Vector#(32, Bit#(17))) fifo_col <- mkPipelineFIFOF;        // [E[0..15] O[0..15]]
   RWire#(Tuple3#(Bit#(2), Bit#(9), Vector#(32, Bit#(16)))) wr_1D <- mkRWire;

   rule write_bram_1D(wr_1D.wget matches tagged Valid .x);
      let sz  = tpl_1(x);
      let idx = tpl_2(x);
      let dat = tpl_3(x);

      case(sz)
         0: begin
           mem[{idx[0], 3'd0}].write(0, {dat[ 5], dat[ 4], dat[ 1], dat[ 0]});
           mem[{idx[0], 3'd1}].write(0, {dat[ 7], dat[ 6], dat[ 3], dat[ 2]});
           mem[{idx[0], 3'd2}].write(0, {dat[13], dat[12], dat[ 9], dat[ 8]});
           mem[{idx[0], 3'd3}].write(0, {dat[15], dat[14], dat[10], dat[11]});
           mem[{idx[0], 3'd4}].write(0, {dat[21], dat[20], dat[17], dat[16]});
           mem[{idx[0], 3'd5}].write(0, {dat[23], dat[22], dat[19], dat[18]});
           mem[{idx[0], 3'd6}].write(0, {dat[29], dat[28], dat[25], dat[24]});
           mem[{idx[0], 3'd7}].write(0, {dat[31], dat[30], dat[27], dat[26]});
         end

         1: begin
           mem[{idx[1:0], 2'd0}].write(0, {dat[ 3], dat[ 2], dat[ 1], dat[ 0]});
           mem[{idx[1:0], 2'd1}].write(0, {dat[ 7], dat[ 6], dat[ 5], dat[ 4]});
           mem[{idx[1:0], 2'd2}].write(0, {dat[11], dat[10], dat[ 9], dat[ 8]});
           mem[{idx[1:0], 2'd3}].write(0, {dat[15], dat[14], dat[13], dat[12]});
         end

         2: begin
           mem[{idx[2:0], 1'd0}].write({2'd0, idx[5:3]}, {dat[ 3], dat[ 2], dat[ 1], dat[ 0]});
           mem[{idx[2:0], 1'd1}].write({2'd0, idx[5:3]}, {dat[ 7], dat[ 6], dat[ 5], dat[ 4]});
         end

         default: begin
            mem[idx[7:4]].write({idx[8],idx[3:0]}, pack(take(dat)));
            $display("[%6d] (%2d,%2d) %04X, %04X, %04X, %04X", cycles, idx[7:4], idx[3:0], dat[0], dat[1], dat[2], dat[3]);
         end
      endcase
   endrule

   rule do_dct_1D(fifo_in.notEmpty);
      let x = fifo_in.first;

      /*
      if (rowIdx == 0) begin
         $write("E: ");
         for(Integer i = 0; i < 32 / 2; i = i + 1) begin
            Bit#(16) tmp = sExtend(x[0][i]);
            $write("%04X, ", tmp);
         end
         $write("\n");
      end
      */

      let y0 = dct_1D[0].dct(take    (x[0]), {rowIdx[3:0], 1'd0});
      let y1 = dct_1D[1].dct(take    (x[1]), {rowIdx[3:0], 1'd0});
      let y2 = dct_1D[2].dct(takeTail(x[0]), {rowIdx[3:0], 1'd1});
      let y3 = dct_1D[3].dct(takeTail(x[1]), {rowIdx[3:0], 1'd1});

      let idxTmpA = rowIdx[3:0];
      let idxTmpB = rowIdx[7:4];
      case(dct_size)
         0: begin
            Vector#(8, Bit#(16)) m0 = takeAt(0, y0);
            Vector#(8, Bit#(16)) m1 = takeAt(0, y1);
            Vector#(8, Bit#(16)) m2 = takeAt(0, y2);
            Vector#(8, Bit#(16)) m3 = takeAt(0, y3);
            wr_1D.wset(tuple3(dct_size, rowIdx, unpack({?, pack(m3), pack(m2), pack(m1), pack(m0)})));

            // Update Row & Col sync
            idxTmpA = idxTmpA | 4'b1110;
            idxTmpB = idxTmpB | 4'b1111;
         end

         1: begin
            Vector#(4, Bit#(16)) m0 = takeAt(8, y0);
            Vector#(4, Bit#(16)) m1 = takeAt(8, y1);
            Vector#(4, Bit#(16)) m2 = takeAt(8, y2);
            Vector#(4, Bit#(16)) m3 = takeAt(8, y3);
            wr_1D.wset(tuple3(dct_size, rowIdx, unpack({?, pack(m3), pack(m2), pack(m1), pack(m0)})));

            // Update Row & Col sync
            idxTmpA = idxTmpA | 4'b1100;
            idxTmpB = idxTmpB | 4'b1100;
         end

         2: begin
            Vector#(2, Bit#(16)) m0 = takeAt(12, y0);
            Vector#(2, Bit#(16)) m1 = takeAt(12, y1);
            Vector#(2, Bit#(16)) m2 = takeAt(12, y2);
            Vector#(2, Bit#(16)) m3 = takeAt(12, y3);
            wr_1D.wset(tuple3(dct_size, rowIdx, unpack({?, pack(m3), pack(m2), pack(m1), pack(m0)})));

            // Update Row & Col sync
            idxTmpA = idxTmpA | 4'b1000;
            idxTmpB = {2'b11, rowIdx[4:3]};
         end

         default: begin
            wr_1D.wset(tuple3(dct_size, rowIdx, unpack({?, y3[14], y2[14], y1[14], y0[14]})));
         end
      endcase

      // Update Row & Col sync
      if (idxTmpA == '1) begin
         fifo_in.deq;
      end
      if (idxTmpB == '1) begin
         w_syncCol <= 1;
      end
      rowIdx <= rowIdx + 1;
   endrule

   rule read_mem;
      for(Integer i = 0; i < 16; i = i + 1) begin
         mem[i].read(colCnt);
      end
      syncCol <= {syncCol[0], w_syncCol};
   endrule

   rule pre_2D({syncCol[1],colCnt[4:0]} != 0);
      Vector#(32, Bit#(16)) x;
      Vector#(32, Bit#(17)) y;

      for(Integer i = 0; i < 32 / 2; i = i + 1) begin
         Vector#(2,Bit#(16)) t = unpack(mem[i].read_resp());
         x[i*2+0] = t[0];
         x[i*2+1] = t[1];
      end

      if (False) begin
         $write("[%6d] INP-2D: ", cycles);
         for(Integer i = 0; i < 32; i = i + 1) begin
            $write("%04X, ", x[i]);
         end
         $write("\n");
         //$write("             -O: ");
         //for(Integer i = 0; i < 16; i = i + 1) begin
         //   $write("%04X, ", x[16+i]);
         //end
         $write("\n");
      end

      for(Integer i = 0; i < 32 / 2; i = i + 1) begin
         y[ 0 + i] = sExtend(x[i]) + sExtend(x[31 - i]);
         y[16 + i] = sExtend(x[i]) - sExtend(x[31 - i]);
      end

      fifo_col.enq(y);
      colCnt <= colCnt + 1;
   endrule

   rule do_dct_2D;
      let x = fifo_col.first;

      if (False) begin
         $write("[%6d] EO-2D: ", cycles);
         for(Integer i = 0; i < 32; i = i + 1) begin
            Bit#(16) t = truncate(x[i]);
            $write("%04X, ", t);
         end
         $write("\n");
      end

      let y0 = dct_2D[0].dct(take    (x), {colIdx, 2'd0});
      let y1 = dct_2D[1].dct(takeTail(x), {colIdx, 2'd1});
      let y2 = dct_2D[2].dct(take    (x), {colIdx, 2'd2});
      let y3 = dct_2D[3].dct(takeTail(x), {colIdx, 2'd3});

      case(dct_size)
         0: begin
            Vector#(8, Bit#(16)) m0 = takeAt(0, y0);
            Vector#(8, Bit#(16)) m1 = takeAt(0, y1);
            Vector#(8, Bit#(16)) m2 = takeAt(0, y2);
            Vector#(8, Bit#(16)) m3 = takeAt(0, y3);
            fifo_out.enq(unpack({?, pack(m3), pack(m2), pack(m1), pack(m0)}));
         end

         1: begin
            Vector#(4, Bit#(16)) m0 = takeAt(8, y0);
            Vector#(4, Bit#(16)) m1 = takeAt(8, y1);
            Vector#(4, Bit#(16)) m2 = takeAt(8, y2);
            Vector#(4, Bit#(16)) m3 = takeAt(8, y3);
            fifo_out.enq(unpack({?, pack(m3), pack(m2), pack(m1), pack(m0)}));
         end

         2: begin
            Vector#(2, Bit#(16)) m0 = takeAt(12, y0);
            Vector#(2, Bit#(16)) m1 = takeAt(12, y1);
            Vector#(2, Bit#(16)) m2 = takeAt(12, y2);
            Vector#(2, Bit#(16)) m3 = takeAt(12, y3);
            fifo_out.enq(unpack({?, pack(m3), pack(m2), pack(m1), pack(m0)}));
         end

         default: begin
            fifo_out.enq(unpack({?, y3[14], y2[14], y1[14], y0[14]}));
         end
      endcase
      //$display("[%6d] %3d: %04X, %04X, %04X, %04X", cycles, colIdx, y0, y1, y2, y3);

      colIdx <= colIdx + 1;
      if (colIdx == '1) begin
         fifo_col.deq;
      end
   endrule

   // External Interface
   method Action setSize(Bit#(2) dsize);
      dct_size <= dsize;
   endmethod

   interface Put io_in = interface Put;
                            method Action put(Vector#(2, Vector#(32, Bit#(9))) x);
                               Vector#(2, Vector#(32, Bit#(10))) y = ?;

                               for(Integer i = 0; i < 32 / 2; i = i + 1) begin
                                  y[0][ 0 + i] = sExtend(x[0][i]) + sExtend(x[0][31 - i]);
                                  y[0][16 + i] = sExtend(x[0][i]) - sExtend(x[0][31 - i]);
                                  y[1][ 0 + i] = sExtend(x[1][i]) + sExtend(x[1][31 - i]);
                                  y[1][16 + i] = sExtend(x[1][i]) - sExtend(x[1][31 - i]);
                               end
                               fifo_in.enq(y);
                            endmethod
                         endinterface;

   interface Get io_out = toGet(fifo_out);

endmodule : mkDct32

`ifdef TEST_BENCH_mkDct32
import "BDPI" function Action dct32_genNew();
import "BDPI" function ActionValue#(Vector#(2, Vector#(32, Bit#(16)))) dct32_getDiff();
import "BDPI" function ActionValue#(Vector#(4, Bit#(16))) dct32_getDct();

(* synthesize *)
module mkTb(Empty);
   FIFOF#(Vector#(2, Vector#(32, Bit#(9)))) fifo_in <- mkPipelineFIFOF;
   FIFOF#(Vector#(4, Bit#(16))) fifo_out <- mkPipelineFIFOF;
   Reg#(Bit#(16)) cycles <- mkReg(0);
   Reg#(Bit#(9))  states <- mkReg(0);
   Reg#(Bit#(8))  passed <- mkReg(0);

   IDct32 dct32 <- mkDct32;

   mkConnection(toGet(fifo_in), dct32.io_in);
   mkConnection(dct32.io_out, toPut(fifo_out));

   rule do_cycles;
      cycles <= cycles + 1;
   endrule

   rule do_init(states == 0);
        dct32_genNew();
        $display("[%6d] Init DCT test data", cycles);
        states <= 1;
   endrule

   rule do_input(states >= 1 && states <= 16);
         let x <- dct32_getDiff;
         Vector#(2, Vector#(32, Bit#(9))) y;
         y[0] = map(truncate, x[0]);
         y[1] = map(truncate, x[1]);

         fifo_in.enq( y );

         $write("[%6d] ", cycles);
         for(Integer i = 0; i < 32; i = i + 1) begin
            $write("%04X, ", x[0][i]);
         end
         $write("\n         ");
         for(Integer i = 0; i < 32; i = i + 1) begin
            $write("%04X, ", x[1][i]);
         end
         $write("\n");
         states <= states + 1;
   endrule

   rule do_check(states >= 17 && states < 17+32*8);
      let x <- dct32_getDct;
      let y = fifo_out.first;

      if (x != y) begin
         $display("Verify failed: %X -> %X", x, y);
         $finish;
      end
      else begin
         $display("[%6d] (%3d) %X pass", cycles, states-17, x);
      end

      fifo_out.deq;
      states <= states + 1;
   endrule

   rule do_end(states >= 17+32*8);
      $display("[%d] All test passed!", passed);
      states <= 0;
      passed <= passed + 1;
      if (passed >= 10)
         $finish;
   endrule

      //states <= (states == 17+32*8) ? 0 : states + 1;
endmodule
`endif

// Copyright (c) 2016 Min Chen
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//

import Vector::*;
import GetPut::*;
import Connectable::*;
import ConfigReg::*;
import FIFOF::*;
import SpecialFIFOs::*;
import BUtils::*;
import BRAMCore::*;
import SMul::*;
import ARAM::*;

Integer g_t32[32][16] =
{
    { 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 },
    { 90, 90, 88, 85, 82, 78, 73, 67, 61, 54, 46, 38, 31, 22, 13,  4 },
    { 90, 87, 80, 70, 57, 43, 25,  9, -9,-25,-43,-57,-70,-80,-87,-90 },
    { 90, 82, 67, 46, 22, -4,-31,-54,-73,-85,-90,-88,-78,-61,-38,-13 },
    { 89, 75, 50, 18,-18,-50,-75,-89,-89,-75,-50,-18, 18, 50, 75, 89 },
    { 88, 67, 31,-13,-54,-82,-90,-78,-46, -4, 38, 73, 90, 85, 61, 22 },
    { 87, 57,  9,-43,-80,-90,-70,-25, 25, 70, 90, 80, 43, -9,-57,-87 },
    { 85, 46,-13,-67,-90,-73,-22, 38, 82, 88, 54, -4,-61,-90,-78,-31 },
    { 83, 36,-36,-83,-83,-36, 36, 83, 83, 36,-36,-83,-83,-36, 36, 83 },
    { 82, 22,-54,-90,-61, 13, 78, 85, 31,-46,-90,-67,  4, 73, 88, 38 },
    { 80,  9,-70,-87,-25, 57, 90, 43,-43,-90,-57, 25, 87, 70, -9,-80 },
    { 78, -4,-82,-73, 13, 85, 67,-22,-88,-61, 31, 90, 54,-38,-90,-46 },
    { 75,-18,-89,-50, 50, 89, 18,-75,-75, 18, 89, 50,-50,-89,-18, 75 },
    { 73,-31,-90,-22, 78, 67,-38,-90,-13, 82, 61,-46,-88, -4, 85, 54 },
    { 70,-43,-87,  9, 90, 25,-80,-57, 57, 80,-25,-90, -9, 87, 43,-70 },
    { 67,-54,-78, 38, 85,-22,-90,  4, 90, 13,-88,-31, 82, 46,-73,-61 },
    { 64,-64,-64, 64, 64,-64,-64, 64, 64,-64,-64, 64, 64,-64,-64, 64 },
    { 61,-73,-46, 82, 31,-88,-13, 90, -4,-90, 22, 85,-38,-78, 54, 67 },
    { 57,-80,-25, 90, -9,-87, 43, 70,-70,-43, 87,  9,-90, 25, 80,-57 },
    { 54,-85, -4, 88,-46,-61, 82, 13,-90, 38, 67,-78,-22, 90,-31,-73 },
    { 50,-89, 18, 75,-75,-18, 89,-50,-50, 89,-18,-75, 75, 18,-89, 50 },
    { 46,-90, 38, 54,-90, 31, 61,-88, 22, 67,-85, 13, 73,-82,  4, 78 },
    { 43,-90, 57, 25,-87, 70,  9,-80, 80, -9,-70, 87,-25,-57, 90,-43 },
    { 38,-88, 73, -4,-67, 90,-46,-31, 85,-78, 13, 61,-90, 54, 22,-82 },
    { 36,-83, 83,-36,-36, 83,-83, 36, 36,-83, 83,-36,-36, 83,-83, 36 },
    { 31,-78, 90,-61,  4, 54,-88, 82,-38,-22, 73,-90, 67,-13,-46, 85 },
    { 25,-70, 90,-80, 43,  9,-57, 87,-87, 57, -9,-43, 80,-90, 70,-25 },
    { 22,-61, 85,-90, 73,-38, -4, 46,-78, 90,-82, 54,-13,-31, 67,-88 },
    { 18,-50, 75,-89, 89,-75, 50,-18,-18, 50,-75, 89,-89, 75,-50, 18 },
    { 13,-38, 61,-78, 88,-90, 85,-73, 54,-31,  4, 22,-46, 67,-82, 90 },
    {  9,-25, 43,-57, 70,-80, 87,-90, 90,-87, 80,-70, 57,-43, 25, -9 },
    {  4,-13, 22,-31, 38,-46, 54,-61, 67,-73, 78,-82, 85,-88, 90,-90 }
};

interface IDct32;
   interface Put#(Vector#(2, Vector#(32, Bit#(9)))) io_in;
   interface Get#(Vector#(4, Bit#(16))) io_out;
endinterface

interface IDct32Core#(numeric type a);
   method Bit#(16) dct(Vector#(16, Bit#(a)) x, Bit#(5) idx);
endinterface

module mkDct32Core(IDct32Core#(a))
   provisos(
      Add#(_x, 9, a),
      Add#(0, 8, b),
      Add#(1, c, TAdd#(a, b)),
      Add#(c, 1, c1),
      Add#(c, 2, c2),
      Add#(a, 10/*(5-1+6)*/, bs),
      Add#(shift, 16, bs),
      Add#(0, TExp#(TSub#(shift, 1)), round)
   );

   Vector#(16, SMUL#(a,b)) smul <- replicateM(mkSMUL);

   method Bit#(16) dct(Vector#(16, Bit#(a)) x, Bit#(5) idx);
      Bit#(c) x0[16];
      for(Integer i = 0; i < 16; i = i + 1)
         x0[i] = smul[i].c(x[i], fromInteger(g_t32[idx][i]));

      // Export 4 of DCT4 result from here
      Bit#(c1) x1[8];
      for(Integer i = 0; i < 8; i = i + 1)
         x1[i] = sExtend(x0[2 * i + 0]) + sExtend(x0[2 * i + 1]);

      // Export DCT8 result from here
      Bit#(c2) x2[4];
      for(Integer i = 0; i < 4; i = i + 1)
         x2[i] = sExtend(x1[2 * i + 0]) + sExtend(x1[2 * i + 1]);

      // Export DCT16 result from here
      Bit#(bs) x3[2];
      for(Integer i = 0; i < 2; i = i + 1)
         x3[i] = sExtend(x2[2 * i + 0]) + sExtend(x2[2 * i + 1]);

      Bit#(bs) sum = sExtend(x3[0]) + sExtend(x3[1]) + fromInteger(valueOf(round));

      return truncate(sum >> valueOf(shift));
   endmethod
endmodule

(* synthesize *)
module mkDct32(IDct32);
   FIFOF#(Vector#(2, Vector#(32, Bit#(10)))) fifo_in <- mkPipelineFIFOF;         // [E[0..15] O[0..15]]
   FIFOF#(Vector#(4, Bit#(16))) fifo_out <- mkPipelineFIFOF;

   Reg#(Bit#(16)) cycles <- mkReg(0);
   rule do_cycles;
      cycles <= cycles + 1;
   endrule

   // Internal
   Vector#(16, BRAM_SDP_PORT#(5,64,6,32))  mem <- replicateM(mkRAM_Nx1);

   Reg#(Bit#(9)) rowIdx <- mkConfigReg(0);
   Reg#(Bit#(6)) colCnt <- mkConfigReg(0);
   Reg#(Bit#(3)) colIdx <- mkConfigReg(0);

   Vector#(4, IDct32Core#(10)) dct_1D <- replicateM(mkDct32Core);
   Vector#(4, IDct32Core#(17)) dct_2D <- replicateM(mkDct32Core);
   Wire#(Bit#(1)) w_syncCol <- mkDWire(0);
   Reg#(Bit#(2)) syncCol <- mkReg(0);
   FIFOF#(Vector#(32, Bit#(17))) fifo_col <- mkPipelineFIFOF;        // [E[0..15] O[0..15]]

   rule do_1D(fifo_in.notEmpty);
      let x = fifo_in.first;

      /*
      if (rowIdx == 0) begin
         $write("E: ");
         for(Integer i = 0; i < 32 / 2; i = i + 1) begin
            Bit#(16) tmp = sExtend(x[0][i]);
            $write("%04X, ", tmp);
         end
         $write("\n");
      end
      */

      let y0 = dct_1D[0].dct(take    (x[0]), {rowIdx[3:0], 1'd0});
      let y1 = dct_1D[1].dct(take    (x[1]), {rowIdx[3:0], 1'd0});
      let y2 = dct_1D[2].dct(takeTail(x[0]), {rowIdx[3:0], 1'd1});
      let y3 = dct_1D[3].dct(takeTail(x[1]), {rowIdx[3:0], 1'd1});

      mem[rowIdx[7:4]].write({rowIdx[8],rowIdx[3:0]}, {y3,y2,y1,y0});
      $display("[%6d] (%2d,%2d) %04X, %04X, %04X, %04X", cycles, rowIdx[7:4], rowIdx[3:0], y0, y1, y2, y3);

      rowIdx <= rowIdx + 1;
      if (rowIdx[3:0] == '1) begin
         fifo_in.deq;
      end

      if (rowIdx[7:4] == '1) begin
         w_syncCol <= 1;
      end
   endrule

   rule read_mem;
      for(Integer i = 0; i < 16; i = i + 1) begin
         mem[i].read(colCnt);
      end
      syncCol <= {syncCol[0], w_syncCol};
   endrule

   rule pre_col({syncCol[1],colCnt[4:0]} != 0);
      Vector#(32, Bit#(16)) x;
      Vector#(32, Bit#(17)) y;

      for(Integer i = 0; i < 32 / 2; i = i + 1) begin
         Vector#(2,Bit#(16)) t = unpack(mem[i].read_resp());
         x[i*2+0] = t[0];
         x[i*2+1] = t[1];
      end

      if (False) begin
         $write("[%6d] INP-2D: ", cycles);
         for(Integer i = 0; i < 32; i = i + 1) begin
            $write("%04X, ", x[i]);
         end
         $write("\n");
         //$write("             -O: ");
         //for(Integer i = 0; i < 16; i = i + 1) begin
         //   $write("%04X, ", x[16+i]);
         //end
         $write("\n");
      end

      for(Integer i = 0; i < 32 / 2; i = i + 1) begin
         y[ 0 + i] = sExtend(x[i]) + sExtend(x[31 - i]);
         y[16 + i] = sExtend(x[i]) - sExtend(x[31 - i]);
      end

      fifo_col.enq(y);
      colCnt <= colCnt + 1;
   endrule

   rule do_2D;
      let x = fifo_col.first;

      if (False) begin
         $write("[%6d] EO-2D: ", cycles);
         for(Integer i = 0; i < 32; i = i + 1) begin
            Bit#(16) t = truncate(x[i]);
            $write("%04X, ", t);
         end
         $write("\n");
      end

      let y0 = dct_2D[0].dct(take    (x), {colIdx, 2'd0});
      let y1 = dct_2D[1].dct(takeTail(x), {colIdx, 2'd1});
      let y2 = dct_2D[2].dct(take    (x), {colIdx, 2'd2});
      let y3 = dct_2D[3].dct(takeTail(x), {colIdx, 2'd3});

      fifo_out.enq(unpack({y3,y2,y1,y0}));
      //$display("[%6d] %3d: %04X, %04X, %04X, %04X", cycles, colIdx, y0, y1, y2, y3);

      colIdx <= colIdx + 1;
      if (colIdx == '1) begin
         fifo_col.deq;
      end
   endrule

   interface Put io_in = interface Put;
                            method Action put(Vector#(2, Vector#(32, Bit#(9))) x);
                               Vector#(2, Vector#(32, Bit#(10))) y = ?;

                               for(Integer i = 0; i < 32 / 2; i = i + 1) begin
                                  y[0][ 0 + i] = sExtend(x[0][i]) + sExtend(x[0][31 - i]);
                                  y[0][16 + i] = sExtend(x[0][i]) - sExtend(x[0][31 - i]);
                                  y[1][ 0 + i] = sExtend(x[1][i]) + sExtend(x[1][31 - i]);
                                  y[1][16 + i] = sExtend(x[1][i]) - sExtend(x[1][31 - i]);
                               end
                               fifo_in.enq(y);
                            endmethod
                         endinterface;
   interface Get io_out = toGet(fifo_out);

endmodule : mkDct32

`ifdef TEST_BENCH_mkDct32
import "BDPI" function Action dct32_genNew();
import "BDPI" function ActionValue#(Vector#(2, Vector#(32, Bit#(16)))) dct32_getDiff();
import "BDPI" function ActionValue#(Vector#(4, Bit#(16))) dct32_getDct();

(* synthesize *)
module mkTb(Empty);
   FIFOF#(Vector#(2, Vector#(32, Bit#(9)))) fifo_in <- mkPipelineFIFOF;
   FIFOF#(Vector#(4, Bit#(16))) fifo_out <- mkPipelineFIFOF;
   Reg#(Bit#(16)) cycles <- mkReg(0);
   Reg#(Bit#(9))  states <- mkReg(0);
   Reg#(Bit#(8))  passed <- mkReg(0);

   IDct32 dct32 <- mkDct32;

   mkConnection(toGet(fifo_in), dct32.io_in);
   mkConnection(dct32.io_out, toPut(fifo_out));

   rule do_cycles;
      cycles <= cycles + 1;
   endrule

   rule do_init(states == 0);
        dct32_genNew();
        $display("[%6d] Init DCT test data", cycles);
        states <= 1;
   endrule

   rule do_input(states >= 1 && states <= 16);
         let x <- dct32_getDiff;
         Vector#(2, Vector#(32, Bit#(9))) y;
         y[0] = map(truncate, x[0]);
         y[1] = map(truncate, x[1]);

         fifo_in.enq( y );

         $write("[%6d] ", cycles);
         for(Integer i = 0; i < 32; i = i + 1) begin
            $write("%04X, ", x[0][i]);
         end
         $write("\n         ");
         for(Integer i = 0; i < 32; i = i + 1) begin
            $write("%04X, ", x[1][i]);
         end
         $write("\n");
         states <= states + 1;
   endrule

   rule do_check(states >= 17 && states < 17+32*8);
      let x <- dct32_getDct;
      let y = fifo_out.first;

      if (x != y) begin
         $display("Verify failed: %X -> %X", x, y);
         $finish;
      end
      else begin
         $display("[%6d] (%3d) %X pass", cycles, states-17, x);
      end

      fifo_out.deq;
      states <= states + 1;
   endrule

   rule do_end(states >= 17+32*8);
      $display("[%d] All test passed!", passed);
      states <= 0;
      passed <= passed + 1;
      if (passed >= 10)
         $finish;
   endrule

      //states <= (states == 17+32*8) ? 0 : states + 1;
endmodule
`endif


connect first master to ACP if USE_ACP is defined

// 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 Clocks::*;
import DefaultValue::*;
import GetPut::*;
import Connectable::*;
import ConnectableWithTrace::*;
import Bscan::*;
import Vector::*;
import PPS7LIB::*;
import Portal::*;
import AxiMasterSlave::*;
import AxiDma::*;
import XilinxCells::*;
import ConnectalXilinxCells::*;
import ConnectalClocks::*;
import AxiBits::*;
import AxiGather::*;

(* always_ready, always_enabled *)
interface Bidir#(numeric type data_width);
    method Action             i(Bit#(data_width) v);
    method Bit#(data_width)   o();
    method Bit#(data_width)   t();
endinterface

interface PS7LIB;
`ifdef PS7EXTENDED
    interface Vector#(2, Pps7Emiocan)  can;
    interface Vector#(4, Pps7Dma)  dma;
    interface Vector#(2, Pps7Emioenet) enet;
    interface Pps7Event            event_;
//    interface Vector#(4,Pps7Fclk_clktrig)fclk_clktrig;
//    interface Pps7Fpga             fpga;
    interface Pps7Ftmd             ftmd;
    interface Pps7Ftmt             ftmt;
    interface Pps7Emiopjtag            pjtag;
    interface Vector#(2, Pps7Emiosdio) sdio;
    interface Vector#(2, Pps7Emiospi)  spi;
//    interface Pps7Sram             sram;
    interface Pps7Emiotrace            trace;
    interface Vector#(2, Pps7Emiottc)  ttc;
    interface Vector#(2, Pps7Emiouart) uart;
    interface Vector#(2, Pps7Emiousb)  usb;
    interface Pps7Emiowdt              wdt;
`endif
    interface Pps7Ddr              ddr;
    method Bit#(4)     fclkclk();
    method Action      fclkclktrign(Bit#(4) v);
    method Bit#(4)     fclkresetn();
    method Action      fpgaidlen(Bit#(1) v);
    interface Pps7Emiogpio             gpio;
    interface Vector#(2, Pps7Emioi2c)  i2c;
    interface Pps7Irq              irq;
    interface Inout#(Bit#(54))     mio;
    interface Pps7Ps               ps;

    interface Vector#(2, AxiMasterCommon#(32,32,12)) m_axi_gp;
    interface Vector#(2, AxiSlaveCommon#(32,32,6,Empty)) s_axi_gp;
    interface Vector#(4, AxiSlaveCommon#(32,64,6,HPType)) s_axi_hp;
    interface AxiSlaveCommon#(32,64,3,ACPType) s_axi_acp;
endinterface

module mkPS7LIB#(Clock axi_clock, Reset axi_reset)(PS7LIB);
    PPS7LIB foo <- mkPPS7LIB(
        axi_clock, axi_clock, axi_clock, axi_clock, axi_clock, axi_clock, axi_clock, axi_clock,
        axi_clock, axi_reset, axi_reset, axi_reset, axi_reset, axi_reset, axi_reset, axi_reset,
        axi_reset, axi_reset);
`ifdef PS7EXTENDED
    Vector#(2, Pps7Emiocan)     vcan;
    Vector#(4, Pps7Dma)     vdma;
    Vector#(2, Pps7Emioenet)     venet;
    Vector#(2, Pps7Emiosdio)     vsdio;
    Vector#(2, Pps7Emiospi)     vspi;
    Vector#(2, Pps7Emiottc)     vttc;
    Vector#(2, Pps7Emiouart)     vuart;
    Vector#(2, Pps7Emiousb)     vusb;
`endif
    Vector#(2, Pps7Emioi2c)     vi2c;
    Vector#(2, AxiMasterCommon#(32,32,12)) vtopm_axi_gp;
    Vector#(2, AxiSlaveCommon#(32,32,6,Empty)) vtops_axi_gp;
    Vector#(1, AxiSlaveCommon#(32,64,3,ACPType)) vtops_axi_acp;
    Vector#(4, AxiSlaveCommon#(32,64,6,HPType)) vtops_axi_hp;

`ifdef PS7EXTENDED
    vcan[0] = foo.emiocan0;
    vcan[1] = foo.emiocan1;
    vdma[0] = foo.dma0;
    vdma[1] = foo.dma1;
    vdma[2] = foo.dma2;
    vdma[3] = foo.dma3;
    venet[0] = foo.emioenet0;
    venet[1] = foo.emioenet1;
    vsdio[0] = foo.emiosdio0;
    vsdio[1] = foo.emiosdio1;
    vspi[0] = foo.emiospi0;
    vspi[1] = foo.emiospi1;
    vttc[0] = foo.emiottc0;
    vttc[1] = foo.emiottc1;
    vuart[0] = foo.emiouart0;
    vuart[1] = foo.emiouart1;
    vusb[0] = foo.emiousb0;
    vusb[1] = foo.emiousb1;
`endif
    vi2c[0] = foo.emioi2c0;
    vi2c[1] = foo.emioi2c1;
    vtopm_axi_gp[0] <- mkAxi3MasterGather(foo.maxigp0, clocked_by axi_clock, reset_by axi_reset);
    vtopm_axi_gp[1] <- mkAxi3MasterGather(foo.maxigp1, clocked_by axi_clock, reset_by axi_reset);
    vtops_axi_gp[0] <- mkAxi3SlaveGather(foo.saxigp0, clocked_by axi_clock, reset_by axi_reset);
    vtops_axi_gp[1] <- mkAxi3SlaveGather(foo.saxigp1, clocked_by axi_clock, reset_by axi_reset);
    vtops_axi_acp[0] <- mkAxi3SlaveGather(foo.saxiacp, clocked_by axi_clock, reset_by axi_reset);
    vtops_axi_hp[0] <- mkAxi3SlaveGather(foo.saxihp0, clocked_by axi_clock, reset_by axi_reset);
    vtops_axi_hp[1] <- mkAxi3SlaveGather(foo.saxihp1, clocked_by axi_clock, reset_by axi_reset);
    vtops_axi_hp[2] <- mkAxi3SlaveGather(foo.saxihp2, clocked_by axi_clock, reset_by axi_reset);
    vtops_axi_hp[3] <- mkAxi3SlaveGather(foo.saxihp3, clocked_by axi_clock, reset_by axi_reset);
    Wire#(Bit#(1)) fpgaidlenw <- mkDWire(1);
    rule fpgaidle;
       foo.fpgaidlen(fpgaidlenw);
    endrule
    rule misc;
       foo.emiosramintin(0);
       // UG585 "fclkclktrign is currently not supported and must be tied to ground"
       foo.fclkclktrign(0);
    endrule

`ifdef PS7EXTENDED
    interface Pps7Can can = vcan;
    interface Pps7Dma     dma = vdma;
    interface Pps7Enet     enet = venet;
    interface Pps7Sdio    sdio = vsdio;
    interface Pps7Spi    spi = vspi;
    interface Pps7Ttc    ttc = vttc;
    interface Pps7Uart    uart = vuart;
    interface Pps7Usb    usb = vusb;
    interface Pps7Event     event_ = foo.event_;
//    interface Pps7Fpga     fpga = foo.fpga;
    interface Pps7Ftmd     ftmd = foo.ftmd;
    interface Pps7Ftmt     ftmt = foo.ftmt;
    interface Pps7Pjtag     pjtag = foo.emiopjtag;
//    interface Pps7Sram     sram = foo.sram;
    interface Pps7Emiotrace     trace = foo.emiotrace;
    interface Pps7Wdt     wdt = foo.emiowdt;
`endif
    interface i2c = vi2c;
    interface ddr = foo.ddr;
    interface fclkclk = foo.fclkclk;
    interface fclkresetn = foo.fclkresetn;
    method Action      fclkclktrign(Bit#(4) v);
        foo.fclkclktrign(v);
    endmethod
    method Action      fpgaidlen(Bit#(1) v);
       fpgaidlenw <= v;
    endmethod
    interface gpio = foo.emiogpio;
    interface irq = foo.irq;
    interface mio = foo.mio;
    interface ps = foo.ps;

    interface m_axi_gp = vtopm_axi_gp;
    interface s_axi_gp = vtops_axi_gp;
    interface s_axi_hp = vtops_axi_hp;
    interface s_axi_acp = vtops_axi_acp[0];
endmodule

interface ZynqPins;
    (* prefix="DDR_Addr" *) interface Inout#(Bit#(15))     a;
    (* prefix="DDR_BankAddr" *) interface Inout#(Bit#(3))     ba;
    (* prefix="DDR_CAS_n" *) interface Inout#(Bit#(1))     casb;
    (* prefix="DDR_CKE" *) interface Inout#(Bit#(1))     cke;
    (* prefix="DDR_CS_n" *) interface Inout#(Bit#(1))     csb;
    (* prefix="DDR_Clk_n" *) interface Inout#(Bit#(1))     ckn;
    (* prefix="DDR_Clk_p" *) interface Inout#(Bit#(1))     ck;
    (* prefix="DDR_DM" *) interface Inout#(Bit#(4))     dm;
    (* prefix="DDR_DQ" *) interface Inout#(Bit#(32))     dq;
    (* prefix="DDR_DQS_n" *) interface Inout#(Bit#(4))     dqsn;
    (* prefix="DDR_DQS_p" *) interface Inout#(Bit#(4))     dqs;
    (* prefix="DDR_DRSTB" *) interface Inout#(Bit#(1))     drstb;
    (* prefix="DDR_ODT" *) interface Inout#(Bit#(1))     odt;
    (* prefix="DDR_RAS_n" *) interface Inout#(Bit#(1))     rasb;
    (* prefix="FIXED_IO_ddr_vrn" *) interface Inout#(Bit#(1))     vrn;
    (* prefix="FIXED_IO_ddr_vrp" *) interface Inout#(Bit#(1))     vrp;
    (* prefix="DDR_WEB" *) interface Inout#(Bit#(1))     web;
    (* prefix="MIO" *)
    interface Inout#(Bit#(54))       mio;
    (* prefix="FIXED_IO_ps" *)
    interface Pps7Ps ps;
endinterface

interface PS7;
    (* prefix="" *)
    interface ZynqPins pins;
    interface Vector#(2, AxiMasterCommon#(32,32,12))     m_axi_gp;
    interface Vector#(2, AxiSlaveCommon#(32,32,6,Empty)) s_axi_gp;
    interface Vector#(4, AxiSlaveCommon#(32,64,6,HPType))   s_axi_hp;
    interface Vector#(1, AxiSlaveCommon#(32,64,3,ACPType))   s_axi_acp;
    method Action                             interrupt(Bit#(1) v);
    interface Vector#(4, Clock) fclkclk;
    interface Vector#(4, Reset) fclkreset;
    interface Vector#(2, Pps7Emioi2c)  i2c;
    interface Clock derivedClock;
    interface Reset derivedReset;
`ifdef PS7EXTENDED
    interface Pps7Emiosdio emiosdio1;
`endif
endinterface

module mkPS7(PS7);
   // B2C converts a bit to a clock, enabling us to break the apparent cycle
   Vector#(4, B2C) b2c <- replicateM(mkB2C());

   // need the bufg here to reduce clock skew
   module mkBufferedClock#(Integer i)(Clock); let c <- mkClockBUFG(clocked_by b2c[i].c); return c; endmodule
   module mkBufferedReset#(Integer i)(Reset); let r <- mkResetBUFG(clocked_by b2c[i].c, reset_by b2c[i].r); return r; endmodule
   Vector#(4, Clock) fclk <- genWithM(mkBufferedClock);
   Vector#(4, Reset) freset <- genWithM(mkBufferedReset);

   Clock single_clock = fclk[0];
`ifdef ZYNQ_NO_RESET
   freset[0]          = noReset;
`endif
   let single_reset   = freset[0];

   ClockGenerator7Params clockParams = defaultValue;
   // input clock 200MHz for speed grade -2, 100MHz for speed grade -1
   // fpll needs to be in the range 600MHz - 1200MHz for either input clock
   //
   // fclkin = 1e9 / mainClockPeriod
   // fpll = 1e9 = mult_f * 1e9 / mainClockPeriod
   // mult_f = mainClockPeriod
   //
   // fclkout0 = 1e9 / divide_f = 1e9 / derivedClockPeriod
   // divide_f = derivedClockPeriod
   //
   clockParams.clkfbout_mult_f       = mainClockPeriod;
   clockParams.clkfbout_phase     = 0.0;
   clockParams.clkfbout_phase     = 0.0;
   clockParams.clkin1_period      = mainClockPeriod;
   clockParams.clkout0_divide_f   = derivedClockPeriod;
   clockParams.clkout0_duty_cycle = 0.5;
   clockParams.clkout0_phase      = 0.0000;
   clockParams.clkout0_buffer     = True;
   clockParams.clkin_buffer = False;
   ClockGenerator7   clockGen <- mkClockGenerator7(clockParams, clocked_by single_clock, reset_by single_reset);
   let derived_clock = clockGen.clkout0;
   let derived_reset_unbuffered <- mkAsyncReset(2, single_reset, derived_clock);
   let derived_reset <- mkResetBUFG(clocked_by derived_clock, reset_by derived_reset_unbuffered);

   PS7LIB ps7 <- mkPS7LIB(single_clock, single_reset, clocked_by single_clock, reset_by single_reset);

   // this rule connects the fclkclk wires to the clock net via B2C
   for (Integer i = 0; i < 4; i = i + 1) begin
      ReadOnly#(Bit#(4)) fclkb;
      ReadOnly#(Bit#(4)) fclkresetnb;
      fclkb       <- mkNullCrossingWire(b2c[i].c, ps7.fclkclk);
      fclkresetnb <- mkNullCrossingWire(b2c[i].c, ps7.fclkresetn);
      rule b2c_rule1;
	 b2c[i].inputclock(fclkb[i]);
	 b2c[i].inputreset(fclkresetnb[i]);
      endrule
      rule issue_rule;
         ps7.s_axi_hp[i].extra.rdissuecap1en(0);
         ps7.s_axi_hp[i].extra.wrissuecap1en(0);
      endrule
   end

   IDELAYCTRL idel <- mkIDELAYCTRL(2, clocked_by fclk[3], reset_by freset[0]);

    rule arb_rule;
        ps7.ddr.arb(4'b0);
    endrule

`ifdef PS7EXTENDED
    interface Pps7Emiosdio emiosdio1 = ps7.sdio[1];
`endif
    interface ZynqPins pins;
    interface a = ps7.ddr.a;
    interface ba = ps7.ddr.ba;
    interface casb = ps7.ddr.casb;
    interface cke = ps7.ddr.cke;
    interface csb = ps7.ddr.csb;
    interface ckn = ps7.ddr.ckn;
    interface ck = ps7.ddr.ckp;
    interface dm = ps7.ddr.dm;
    interface dq = ps7.ddr.dq;
    interface dqsn = ps7.ddr.dqsn;
    interface dqs = ps7.ddr.dqsp;
    interface drstb = ps7.ddr.drstb;
    interface odt = ps7.ddr.odt;
    interface rasb = ps7.ddr.rasb;
    interface vrn = ps7.ddr.vrn;
    interface vrp = ps7.ddr.vrp;
    interface web = ps7.ddr.web;
    interface mio = ps7.mio;
    interface ps = ps7.ps;
    endinterface
    interface m_axi_gp = ps7.m_axi_gp;
    interface s_axi_gp = ps7.s_axi_gp;
    interface s_axi_hp = ps7.s_axi_hp;
    interface fclkclk = fclk;
    interface fclkreset = freset;
    interface derivedClock = derived_clock;
    interface derivedReset = derived_reset;
    method Action interrupt(Bit#(1) v);
        ps7.irq.f2p({19'b0, v});
    endmethod
    interface i2c = ps7.i2c;
   interface s_axi_acp = cons(ps7.s_axi_acp, nil);
endmodule

`ifdef USE_ACP
typedef 1 NumAcp;
`else
typedef 0 NumAcp;
`endif

instance ConnectableWithTrace#(PS7, ConnectalTop#(32,64,ipins,nMasters), BscanTop);
   module mkConnectionWithTrace#(PS7 ps7, ConnectalTop#(32,64,ipins,nMasters) top, BscanTop bscan)(Empty);

      Axi3Slave#(32,32,12) ctrl <- mkAxiDmaSlave(top.slave);
      mkConnectionWithTrace(ps7.m_axi_gp[0].client, ctrl, bscan);

`ifdef USE_ACP
      begin
	 Axi3Master#(32,64,3) acp_m_axi <- mkAxiDmaMaster(top.masters[0]);
	 mkConnection(acp_m_axi, ps7.s_axi_acp[0].server);
      end
      rule acp_aruser;
	 ps7.s_axi_acp[0].extra.aruser(5'h1f);
      endrule
      rule acp_awuser;
	 ps7.s_axi_acp[0].extra.awuser(5'h1f);
      endrule
`endif
      module mkAxiMasterConnection#(Integer i)(Axi3Master#(32,64,6));
	 let m_axi <- mkAxiDmaMaster(top.masters[i+valueOf(NumAcp)]);
	 mkConnection(m_axi, ps7.s_axi_hp[i].server);
	 return m_axi;
      endmodule
      Vector#(TSub#(nMasters,NumAcp), Axi3Master#(32,64,6)) m_axis <- genWithM(mkAxiMasterConnection);

   endmodule
endinstance

// 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 Clocks::*;
import DefaultValue::*;
import GetPut::*;
import Connectable::*;
import ConnectableWithTrace::*;
import Bscan::*;
import Vector::*;
import PPS7LIB::*;
import Portal::*;
import AxiMasterSlave::*;
import AxiDma::*;
import XilinxCells::*;
import ConnectalXilinxCells::*;
import ConnectalClocks::*;
import AxiBits::*;
import AxiGather::*;

(* always_ready, always_enabled *)
interface Bidir#(numeric type data_width);
    method Action             i(Bit#(data_width) v);
    method Bit#(data_width)   o();
    method Bit#(data_width)   t();
endinterface

interface PS7LIB;
`ifdef PS7EXTENDED
    interface Vector#(2, Pps7Emiocan)  can;
    interface Vector#(4, Pps7Dma)  dma;
    interface Vector#(2, Pps7Emioenet) enet;
    interface Pps7Event            event_;
//    interface Vector#(4,Pps7Fclk_clktrig)fclk_clktrig;
//    interface Pps7Fpga             fpga;
    interface Pps7Ftmd             ftmd;
    interface Pps7Ftmt             ftmt;
    interface Pps7Emiopjtag            pjtag;
    interface Vector#(2, Pps7Emiosdio) sdio;
    interface Vector#(2, Pps7Emiospi)  spi;
//    interface Pps7Sram             sram;
    interface Pps7Emiotrace            trace;
    interface Vector#(2, Pps7Emiottc)  ttc;
    interface Vector#(2, Pps7Emiouart) uart;
    interface Vector#(2, Pps7Emiousb)  usb;
    interface Pps7Emiowdt              wdt;
`endif
    interface Pps7Ddr              ddr;
    method Bit#(4)     fclkclk();
    method Action      fclkclktrign(Bit#(4) v);
    method Bit#(4)     fclkresetn();
    method Action      fpgaidlen(Bit#(1) v);
    interface Pps7Emiogpio             gpio;
    interface Vector#(2, Pps7Emioi2c)  i2c;
    interface Pps7Irq              irq;
    interface Inout#(Bit#(54))     mio;
    interface Pps7Ps               ps;

    interface Vector#(2, AxiMasterCommon#(32,32,12)) m_axi_gp;
    interface Vector#(2, AxiSlaveCommon#(32,32,6,Empty)) s_axi_gp;
    interface Vector#(4, AxiSlaveCommon#(32,64,6,HPType)) s_axi_hp;
    interface AxiSlaveCommon#(32,64,3,ACPType) s_axi_acp;
endinterface

module mkPS7LIB#(Clock axi_clock, Reset axi_reset)(PS7LIB);
    PPS7LIB foo <- mkPPS7LIB(
        axi_clock, axi_clock, axi_clock, axi_clock, axi_clock, axi_clock, axi_clock, axi_clock,
        axi_clock, axi_reset, axi_reset, axi_reset, axi_reset, axi_reset, axi_reset, axi_reset,
        axi_reset, axi_reset);
`ifdef PS7EXTENDED
    Vector#(2, Pps7Emiocan)     vcan;
    Vector#(4, Pps7Dma)     vdma;
    Vector#(2, Pps7Emioenet)     venet;
    Vector#(2, Pps7Emiosdio)     vsdio;
    Vector#(2, Pps7Emiospi)     vspi;
    Vector#(2, Pps7Emiottc)     vttc;
    Vector#(2, Pps7Emiouart)     vuart;
    Vector#(2, Pps7Emiousb)     vusb;
`endif
    Vector#(2, Pps7Emioi2c)     vi2c;
    Vector#(2, AxiMasterCommon#(32,32,12)) vtopm_axi_gp;
    Vector#(2, AxiSlaveCommon#(32,32,6,Empty)) vtops_axi_gp;
    Vector#(1, AxiSlaveCommon#(32,64,3,ACPType)) vtops_axi_acp;
    Vector#(4, AxiSlaveCommon#(32,64,6,HPType)) vtops_axi_hp;

`ifdef PS7EXTENDED
    vcan[0] = foo.emiocan0;
    vcan[1] = foo.emiocan1;
    vdma[0] = foo.dma0;
    vdma[1] = foo.dma1;
    vdma[2] = foo.dma2;
    vdma[3] = foo.dma3;
    venet[0] = foo.emioenet0;
    venet[1] = foo.emioenet1;
    vsdio[0] = foo.emiosdio0;
    vsdio[1] = foo.emiosdio1;
    vspi[0] = foo.emiospi0;
    vspi[1] = foo.emiospi1;
    vttc[0] = foo.emiottc0;
    vttc[1] = foo.emiottc1;
    vuart[0] = foo.emiouart0;
    vuart[1] = foo.emiouart1;
    vusb[0] = foo.emiousb0;
    vusb[1] = foo.emiousb1;
`endif
    vi2c[0] = foo.emioi2c0;
    vi2c[1] = foo.emioi2c1;
    vtopm_axi_gp[0] <- mkAxi3MasterGather(foo.maxigp0, clocked_by axi_clock, reset_by axi_reset);
    vtopm_axi_gp[1] <- mkAxi3MasterGather(foo.maxigp1, clocked_by axi_clock, reset_by axi_reset);
    vtops_axi_gp[0] <- mkAxi3SlaveGather(foo.saxigp0, clocked_by axi_clock, reset_by axi_reset);
    vtops_axi_gp[1] <- mkAxi3SlaveGather(foo.saxigp1, clocked_by axi_clock, reset_by axi_reset);
    vtops_axi_acp[0] <- mkAxi3SlaveGather(foo.saxiacp, clocked_by axi_clock, reset_by axi_reset);
    vtops_axi_hp[0] <- mkAxi3SlaveGather(foo.saxihp0, clocked_by axi_clock, reset_by axi_reset);
    vtops_axi_hp[1] <- mkAxi3SlaveGather(foo.saxihp1, clocked_by axi_clock, reset_by axi_reset);
    vtops_axi_hp[2] <- mkAxi3SlaveGather(foo.saxihp2, clocked_by axi_clock, reset_by axi_reset);
    vtops_axi_hp[3] <- mkAxi3SlaveGather(foo.saxihp3, clocked_by axi_clock, reset_by axi_reset);
    Wire#(Bit#(1)) fpgaidlenw <- mkDWire(1);
    rule fpgaidle;
       foo.fpgaidlen(fpgaidlenw);
    endrule
    rule misc;
       foo.emiosramintin(0);
       // UG585 "fclkclktrign is currently not supported and must be tied to ground"
       foo.fclkclktrign(0);
    endrule

`ifdef PS7EXTENDED
    interface Pps7Can can = vcan;
    interface Pps7Dma     dma = vdma;
    interface Pps7Enet     enet = venet;
    interface Pps7Sdio    sdio = vsdio;
    interface Pps7Spi    spi = vspi;
    interface Pps7Ttc    ttc = vttc;
    interface Pps7Uart    uart = vuart;
    interface Pps7Usb    usb = vusb;
    interface Pps7Event     event_ = foo.event_;
//    interface Pps7Fpga     fpga = foo.fpga;
    interface Pps7Ftmd     ftmd = foo.ftmd;
    interface Pps7Ftmt     ftmt = foo.ftmt;
    interface Pps7Pjtag     pjtag = foo.emiopjtag;
//    interface Pps7Sram     sram = foo.sram;
    interface Pps7Emiotrace     trace = foo.emiotrace;
    interface Pps7Wdt     wdt = foo.emiowdt;
`endif
    interface i2c = vi2c;
    interface ddr = foo.ddr;
    interface fclkclk = foo.fclkclk;
    interface fclkresetn = foo.fclkresetn;
    method Action      fclkclktrign(Bit#(4) v);
        foo.fclkclktrign(v);
    endmethod
    method Action      fpgaidlen(Bit#(1) v);
       fpgaidlenw <= v;
    endmethod
    interface gpio = foo.emiogpio;
    interface irq = foo.irq;
    interface mio = foo.mio;
    interface ps = foo.ps;

    interface m_axi_gp = vtopm_axi_gp;
    interface s_axi_gp = vtops_axi_gp;
    interface s_axi_hp = vtops_axi_hp;
    interface s_axi_acp = vtops_axi_acp[0];
endmodule

interface ZynqPins;
    (* prefix="DDR_Addr" *) interface Inout#(Bit#(15))     a;
    (* prefix="DDR_BankAddr" *) interface Inout#(Bit#(3))     ba;
    (* prefix="DDR_CAS_n" *) interface Inout#(Bit#(1))     casb;
    (* prefix="DDR_CKE" *) interface Inout#(Bit#(1))     cke;
    (* prefix="DDR_CS_n" *) interface Inout#(Bit#(1))     csb;
    (* prefix="DDR_Clk_n" *) interface Inout#(Bit#(1))     ckn;
    (* prefix="DDR_Clk_p" *) interface Inout#(Bit#(1))     ck;
    (* prefix="DDR_DM" *) interface Inout#(Bit#(4))     dm;
    (* prefix="DDR_DQ" *) interface Inout#(Bit#(32))     dq;
    (* prefix="DDR_DQS_n" *) interface Inout#(Bit#(4))     dqsn;
    (* prefix="DDR_DQS_p" *) interface Inout#(Bit#(4))     dqs;
    (* prefix="DDR_DRSTB" *) interface Inout#(Bit#(1))     drstb;
    (* prefix="DDR_ODT" *) interface Inout#(Bit#(1))     odt;
    (* prefix="DDR_RAS_n" *) interface Inout#(Bit#(1))     rasb;
    (* prefix="FIXED_IO_ddr_vrn" *) interface Inout#(Bit#(1))     vrn;
    (* prefix="FIXED_IO_ddr_vrp" *) interface Inout#(Bit#(1))     vrp;
    (* prefix="DDR_WEB" *) interface Inout#(Bit#(1))     web;
    (* prefix="MIO" *)
    interface Inout#(Bit#(54))       mio;
    (* prefix="FIXED_IO_ps" *)
    interface Pps7Ps ps;
endinterface

interface PS7;
    (* prefix="" *)
    interface ZynqPins pins;
    interface Vector#(2, AxiMasterCommon#(32,32,12))     m_axi_gp;
    interface Vector#(2, AxiSlaveCommon#(32,32,6,Empty)) s_axi_gp;
    interface Vector#(4, AxiSlaveCommon#(32,64,6,HPType))   s_axi_hp;
    method Action                             interrupt(Bit#(1) v);
    interface Vector#(4, Clock) fclkclk;
    interface Vector#(4, Reset) fclkreset;
    interface Vector#(2, Pps7Emioi2c)  i2c;
    interface Clock derivedClock;
    interface Reset derivedReset;
`ifdef PS7EXTENDED
    interface Pps7Emiosdio emiosdio1;
`endif
endinterface

module mkPS7(PS7);
   // B2C converts a bit to a clock, enabling us to break the apparent cycle
   Vector#(4, B2C) b2c <- replicateM(mkB2C());

   // need the bufg here to reduce clock skew
   module mkBufferedClock#(Integer i)(Clock); let c <- mkClockBUFG(clocked_by b2c[i].c); return c; endmodule
   module mkBufferedReset#(Integer i)(Reset); let r <- mkResetBUFG(clocked_by b2c[i].c, reset_by b2c[i].r); return r; endmodule
   Vector#(4, Clock) fclk <- genWithM(mkBufferedClock);
   Vector#(4, Reset) freset <- genWithM(mkBufferedReset);

   Clock single_clock = fclk[0];
`ifdef ZYNQ_NO_RESET
   freset[0]          = noReset;
`endif
   let single_reset   = freset[0];

   ClockGenerator7Params clockParams = defaultValue;
   // input clock 200MHz for speed grade -2, 100MHz for speed grade -1
   // fpll needs to be in the range 600MHz - 1200MHz for either input clock
   //
   // fclkin = 1e9 / mainClockPeriod
   // fpll = 1e9 = mult_f * 1e9 / mainClockPeriod
   // mult_f = mainClockPeriod
   //
   // fclkout0 = 1e9 / divide_f = 1e9 / derivedClockPeriod
   // divide_f = derivedClockPeriod
   //
   clockParams.clkfbout_mult_f       = mainClockPeriod;
   clockParams.clkfbout_phase     = 0.0;
   clockParams.clkfbout_phase     = 0.0;
   clockParams.clkin1_period      = mainClockPeriod;
   clockParams.clkout0_divide_f   = derivedClockPeriod;
   clockParams.clkout0_duty_cycle = 0.5;
   clockParams.clkout0_phase      = 0.0000;
   clockParams.clkout0_buffer     = True;
   clockParams.clkin_buffer = False;
   ClockGenerator7   clockGen <- mkClockGenerator7(clockParams, clocked_by single_clock, reset_by single_reset);
   let derived_clock = clockGen.clkout0;
   let derived_reset_unbuffered <- mkAsyncReset(2, single_reset, derived_clock);
   let derived_reset <- mkResetBUFG(clocked_by derived_clock, reset_by derived_reset_unbuffered);

   PS7LIB ps7 <- mkPS7LIB(single_clock, single_reset, clocked_by single_clock, reset_by single_reset);

   // this rule connects the fclkclk wires to the clock net via B2C
   for (Integer i = 0; i < 4; i = i + 1) begin
      ReadOnly#(Bit#(4)) fclkb;
      ReadOnly#(Bit#(4)) fclkresetnb;
      fclkb       <- mkNullCrossingWire(b2c[i].c, ps7.fclkclk);
      fclkresetnb <- mkNullCrossingWire(b2c[i].c, ps7.fclkresetn);
      rule b2c_rule1;
	 b2c[i].inputclock(fclkb[i]);
	 b2c[i].inputreset(fclkresetnb[i]);
      endrule
      rule issue_rule;
         ps7.s_axi_hp[i].extra.rdissuecap1en(0);
         ps7.s_axi_hp[i].extra.wrissuecap1en(0);
      endrule
   end

   IDELAYCTRL idel <- mkIDELAYCTRL(2, clocked_by fclk[3], reset_by freset[0]);

    rule arb_rule;
        ps7.ddr.arb(4'b0);
    endrule

`ifdef PS7EXTENDED
    interface Pps7Emiosdio emiosdio1 = ps7.sdio[1];
`endif
    interface ZynqPins pins;
    interface a = ps7.ddr.a;
    interface ba = ps7.ddr.ba;
    interface casb = ps7.ddr.casb;
    interface cke = ps7.ddr.cke;
    interface csb = ps7.ddr.csb;
    interface ckn = ps7.ddr.ckn;
    interface ck = ps7.ddr.ckp;
    interface dm = ps7.ddr.dm;
    interface dq = ps7.ddr.dq;
    interface dqsn = ps7.ddr.dqsn;
    interface dqs = ps7.ddr.dqsp;
    interface drstb = ps7.ddr.drstb;
    interface odt = ps7.ddr.odt;
    interface rasb = ps7.ddr.rasb;
    interface vrn = ps7.ddr.vrn;
    interface vrp = ps7.ddr.vrp;
    interface web = ps7.ddr.web;
    interface mio = ps7.mio;
    interface ps = ps7.ps;
    endinterface
    interface m_axi_gp = ps7.m_axi_gp;
    interface s_axi_gp = ps7.s_axi_gp;
    interface s_axi_hp = ps7.s_axi_hp;
    interface fclkclk = fclk;
    interface fclkreset = freset;
    interface derivedClock = derived_clock;
    interface derivedReset = derived_reset;
    method Action interrupt(Bit#(1) v);
        ps7.irq.f2p({19'b0, v});
    endmethod
    interface i2c = ps7.i2c;
endmodule

instance ConnectableWithTrace#(PS7, ConnectalTop#(32,64,ipins,nMasters), BscanTop);
   module mkConnectionWithTrace#(PS7 ps7, ConnectalTop#(32,64,ipins,nMasters) top, BscanTop bscan)(Empty);

      Axi3Slave#(32,32,12) ctrl <- mkAxiDmaSlave(top.slave);
      mkConnectionWithTrace(ps7.m_axi_gp[0].client, ctrl, bscan);

      module mkAxiMasterConnection#(Integer i)(Axi3Master#(32,64,6));
	 let m_axi <- mkAxiDmaMaster(top.masters[i]);
	 mkConnection(m_axi, ps7.s_axi_hp[i].server);
	 return m_axi;
      endmodule
      Vector#(nMasters, Axi3Master#(32,64,6)) m_axis <- genWithM(mkAxiMasterConnection);

   endmodule
endinstance