-------------------------------------------------------------------------------
-- 
--  COPYRIGHT (c) 2006 BY VERIEN DESIGN GROUP, LLC, CONCORD, MA 01742
--
--  ALL RIGHTS RESERVED
--  
--  This  software is furnished under a license and may be used and
--  copied only in accordance with the terms of such license and with
--  the inclusion of the above copyright notice. This software or any
--  other copies thereof, may not be provided or otherwise made
--  available to anyone other than the licensee.  Title to and owner-
--  ship of this software remains with Verien Design Group.
--  
--  The information in this document is subject to change without
--  notice and should not be construed as a commitment by Verien.
-------------------------------------------------------------------------------
-- Title         : pci cycles file
-- Project       : 
-------------------------------------------------------------------------------
-- File          : pcicycs.vhd
-- Author        : David J. Matthews

-------------------------------------------------------------------------------
-- Description : This file is a "pci cycs" file; a package that gets
-- compiled into package design unit "pcicycs". It executes PCI bus
-- cycles through pci_initiator_model.  This particular set of tests
-- are used to simulate acquisitions on the frame grabber.
--
-- This test is used for running subtests on different display formats
-- (8, 16, and 32 bit-per-pixel) and at different pixel and line
-- subsamples (1 through 16).  These are specified by running either
-- the BASIC_ACQ test (which is 8 BPP), the DISPLAY_FORMAT_16BPP test
-- or the DISPLAY_FORMAT_32BPP test.  The tests to run are specified
-- in the SUBTEST_LIST constant array below.  Another array, the
-- SUBSAMPLE_ARRAY is associated with the SUBTEST_LIST array and used
-- to specify the sub-sample for the particular test.  There is a
-- one-to-one correspondence between the SUBTEST_LIST entries and the
-- SUBSAMPLE_ARRAY entries.
--
-- A fourth test, the LUT_ACQ test fills a pattern in the lookup
-- table, performs an acquisition, and checks the data.
--
-- All of these tests enable "block mode" in the video chip which
-- muxes an 8-bit counter into the video data path.  The data pattern
-- is calculated by the getcheckdata procedure and checked in a FOR
-- loop which is used for PCI burst reads.
-------------------------------------------------------------------------------

LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.std_logic_unsigned.ALL;
USE IEEE.std_logic_arith.ALL;
USE WORK.papsubs.ALL;
USE WORK.pci_pack.ALL;
USE WORK.pcidefs.ALL;
USE WORK.fg_simpack.ALL;
USE WORK.scan_params.ALL;
USE WORK.camera_defs.ALL;

PACKAGE pcicycs IS

    -- The following type declaration (SUBTESTS_TYPE) specifies all of the subtests within
    -- this module.  The constant SUBTESTS is an array of subtests to be executed in the
    -- order that they are specified.  Note that all tests are preceded by a call to pci_init
    -- which is used to setup the BARs.
    TYPE subtest_type IS (basic_acq, display_format_16bpp, display_format_32bpp, lut_acq); -- the subtests
    
    TYPE subtest_array_type IS ARRAY (INTEGER RANGE <>) OF subtest_type;

    -- Place any subtests (which are values of SUBTEST_TYPE) in the SUBTEST_LIST array.  The
    -- subtests specified here will be executed in order.
    CONSTANT subtest_list : subtest_array_type := (basic_acq, basic_acq, basic_acq, basic_acq, basic_acq,
                                                   display_format_16bpp, display_format_16bpp,
                                                   display_format_16bpp, display_format_16bpp,
                                                   display_format_16bpp, 
                                                   display_format_32bpp, display_format_32bpp,
                                                   display_format_32bpp, display_format_32bpp,
                                                   display_format_32bpp);

    TYPE subsample_array_type IS ARRAY (subtest_list'RANGE) OF INTEGER RANGE 1 TO 16;
    
    -- This list is for the pixel subsample factors.  Each entry is
    -- associated with a subtest entry in the SUBTEST_LIST array
    -- above.  Therefore, the number of entries in this array must
    -- equal the numberof entries in SUBTEST_LIST.  Valid values for
    -- the subsample factors are 1-16 (this value gets adjusted down
    -- by 1 before loading in the register).
    CONSTANT subsample_array : subsample_array_type := (1, 2, 3, 14, 16,
                                                        1, 2, 3, 14, 16,
                                                        2, 3, 7, 14, 16);
    
    
    PROCEDURE initcycs (SIGNAL cyc : INOUT cycrecord;
                        SIGNAL ctlin : IN ctlin_record;  -- control bus in
			CONSTANT readdata : IN slv32_array(0 TO 15));


    
END pcicycs;


PACKAGE BODY pcicycs IS

    PROCEDURE initcycs (SIGNAL   cyc      : INOUT cycrecord;
                        SIGNAL   ctlin    : IN    ctlin_record;  -- control bus in
                        CONSTANT readdata : IN slv32_array(0 TO 15)) IS

        VARIABLE debug             : BOOLEAN := FALSE;  -- set true for per pixel groups debug msgs
        VARIABLE data              : int     := 0;  -- temp storage
        VARIABLE data_slv          : slv32;  -- temp storage
        VARIABLE data_ptn          : slv8;   -- simply stores the data pattern locally
        VARIABLE disp_control_reg  : int;  -- contents of display control register
        VARIABLE control           : control_record;  -- controls the getcheckdata procedure
        VARIABLE seed              : int;  -- random number seed (not used yet)
        VARIABLE addr_offset1      : int     := 0;  -- temp address storage
        VARIABLE addr_offset2      : int     := 0;  -- temp address storage
        VARIABLE index_offset      : int     := 0;
        VARIABLE pixgroups         : int     := 0;  -- padded no of 4-pixel groups / scan line
        VARIABLE real_pixgroups    : int     := 0;  -- real no of 4-pixel groups / scan line
        VARIABLE pixel             : int     := 0;  -- pixel count within scan line
        VARIABLE starttime         : TIME    := 0 ns;
        VARIABLE burst_enable      : BOOLEAN := TRUE;  -- flag indicating whether to perform a burst op
        CONSTANT timeout           : TIME    := 1.5 ms;  -- timeout
        VARIABLE pixmult           : int     := 1;  -- pixel multiplier factor (for pixel
                                        -- expansion in 16, 32 BPP modes)
        VARIABLE firstime          : BOOLEAN := TRUE;
        VARIABLE subsample_factor  : int;  -- subsample factor
        VARIABLE no_of_lines       : int;  -- adjusted no of lines
        VARIABLE no_of_pixels      : int;  -- adjusted no of pixels which is stored in the h/w
                                        -- (double-word aligned)
        VARIABLE total_pixels      : int;  -- total programmed pixels
        VARIABLE real_no_of_pixels : int;  -- the real number of pixels (used to check the data)

        -- The following record contains the timing in the form of SCAN lists and register values
        -- which are loaded into the SCAN for the test.  These are located in video_params.vhd.
        VARIABLE video_timing : video_record_ptr := NEW video_record'(testni_timing);

    BEGIN

        ---------------------------------------------------------------------------------------
        -- CAMERA RESET, PCI INITIALIZATION
        ---------------------------------------------------------------------------------------
        -- THIS SECTION PERFORMS A CAMERA RESET TO GET THE CAMERA PIXEL CLOCK RUNNING AND THEN
        -- INITIALIZES THE PCI BUS.
        
        -- Set up the basic model switches:

        -- enable read data checking:
        cyc.readcheck <= TRUE;

        -- don't mask data during comparisons:
        cyc.mask <= 16#FFFFFFFF#;

        -- assert all byte enables from the bus model:
        cyc.byte_enables <= (OTHERS => '0');

        -- set the debug message level:
        cyc.ctlout.pcimon_debug_level <= NOTE;  -- turn on all pcimon debug messages

        -- disable the camera pixel clock driver for this test (we don't use the camera model
        -- here);
        cyc.ctlout.enable_camera <= FALSE;
        
        -- Initialize PCI config space:
        init_pci(cyc);

        
        ---------------------------------------------------------------------------------------
        -- SUBTEST SETUP
        ---------------------------------------------------------------------------------------
        -- THIS SECTION SETS UP THE REQUESTED SUBTEST, EITHER 8 BPP (BASIC ACQ), 16 BPP, OR 32
        -- BPP AND AT THE REQUESTED SUBSAMPLE.

        
        FOR subtest IN subtest_list'RANGE LOOP

            -- Note that these tests have much common code and so first
            -- set up some variables and then run through the common stuff.

            ---------------------------------------------------------------------------------------
            -- Basic Test Mode Acquisition Test
            ---------------------------------------------------------------------------------------
            IF subtest_list(subtest) = basic_acq THEN
                
                report_test("STARTING BASIC TEST MODE ACQUISITION");
                pixmult := 1;           -- set up the pixel multiplier factor for 1
                -- load the display control register with the display format
                -- and the value of the subsample from the subsample array:
                ASSERT FALSE REPORT cr & "  SUBSAMPLE SET TO " & INTEGER'image(subsample_array(subtest))
                    SEVERITY NOTE;
                disp_control_reg := format_8bpp + (subsample_array(subtest)-1)*16;
                control.display_format := fmt_8bpp;
                control.pattern := incr;
                
            END IF;
            
            ---------------------------------------------------------------------------------------
            -- 16 Bit Per Pixel Display Format Acquisition Test
            ---------------------------------------------------------------------------------------
            IF subtest_list(subtest) = display_format_16bpp THEN
                
                report_test("STARTING 16BPP DISPLAY FORMAT ACQUISITION TEST");
                pixmult := 2;           -- set up the pixel multiplier factor for 1
                -- load the display control register with the display format
                -- and the value of the subsample from the subsample array:
                disp_control_reg := format_16bpp + (subsample_array(subtest)-1)*16;
                ASSERT FALSE REPORT "  SUBSAMPLE SET TO " & INTEGER'image(subsample_array(subtest))
                    SEVERITY NOTE;
                control.display_format := fmt_16bpp;
                control.pattern := incr;

            END IF;
            
            ---------------------------------------------------------------------------------------
            -- 32 Bit Per Pixel Display Format Acquisition Test
            ---------------------------------------------------------------------------------------
            IF subtest_list(subtest) = display_format_32bpp THEN
                
                report_test("STARTING 32BPP DISPLAY FORMAT ACQUISITION TEST");
                pixmult := 4;           -- set up the pixel multiplier factor for 1
                -- load the display control register with the display format
                -- and the value of the subsample from the subsample array:
                disp_control_reg := format_32bpp + (subsample_array(subtest)-1)*16;
                ASSERT FALSE REPORT "  SUBSAMPLE SET TO " & INTEGER'image(subsample_array(subtest))
                    SEVERITY NOTE;
                control.display_format := fmt_32bpp;
                control.pattern := incr;

            END IF;
            
            ---------------------------------------------------------------------------------------
            -- LUT Acquisition Test
            ---------------------------------------------------------------------------------------

            -- This test programs the LUT with a pattern, performs an acquisition, then check
            IF subtest_list(subtest) = lut_acq THEN
                
                report_test("STARTING LOOKUP TABLE ACQUISITION TEST");
                pixmult := 1;           -- set up the pixel multiplier factor for 1
                -- load the display control register with the display format
                -- and the value of the subsample from the subsample array:
                ASSERT FALSE REPORT cr & "  SUBSAMPLE SET TO " & INTEGER'image(subsample_array(subtest))
                    SEVERITY NOTE;
                disp_control_reg := format_8bpp + (subsample_array(subtest)-1)*16;
                control.display_format := fmt_8bpp;

                -- control.fixed_data is used to hold the data pattern and is passed to getcheckdata to
                -- check the data against a fixed pattern.  This pattern is programmed into the
                -- LUT and so the entire image should consist of this pattern.
                control.fixed_data := x"A55AA55A";
                control.pattern := fixed;

                -- initilialize the LUT with the pattern
                FOR i IN 0 TO 127 LOOP
                    memwrite(fg_bar1_base + scan0_base + lut_base + i*4, control.fixed_data, cyc);
                END LOOP;
                
                
            END IF;                     -- lut acq test

            
            
            -----------------------------------------------------------------------------------
            -- REGISTER INITIALIZATION
            -----------------------------------------------------------------------------------
            -- IN THIS SECTION WE FIRST SETUP THE SCAN BY CALLING AN
            -- EXTERNAL PROCEDURE init_scan_params.  PART OF THIS
            -- INITIALIZATION IS THE CALCULATION OF THE NUMBER OF SCAN
            -- LINES AND NUMBER OF PIXELS TO TEST.
            
            addr_offset1 := fg_bar1_base + scan0_base;

            IF firstime THEN            
                
                init_scan_params(video_timing, addr_offset1, cyc);
                
                -- set up the channel control register for test mode:
                memwrite(fg_bar1_base + scan0_base + channel_control_reg,
                         (clken_clear_bit => '1', test_bit => '1',
                          acquire_enable_bit => '1', interlaced_bit => '0',
                          pad_enable_bit => '1', OTHERS => '0'), cyc);

                -- set up the ADMA cmd reg for block mode DMA (start bit not written yet):
                memwrite(fg_bar0_base + ch0_base + admacmd,
                         acquire_enable + even + dma_threshold, cyc);
                
                firstime := FALSE;
            END IF;

            
            -- set up the Acq DMA PCI address reg
            memwrite(fg_bar0_base + ch0_base + admapa, tar_data_base, cyc);
            
            -- calculate the number of lines to program into the video chip and to check based
            -- upon the subsampling. First we get the raw number of lines from the video
            -- record.  This is divided by 16 because it is in 1/16th line accuracy in the scan:
            no_of_lines := (video_timing.vthresh(17) - video_timing.vthresh(16))/16;

            -- Next we divide by the sub-sample factor.  If it is not evenly divisible then we
            -- have to round up (add 1):
            IF no_of_lines REM subsample_array(subtest) = 0 THEN
                no_of_lines := no_of_lines/subsample_array(subtest);
            ELSE
                no_of_lines := (no_of_lines/subsample_array(subtest)) + 1;
            END IF;

            ASSERT FALSE REPORT cr & " THIS TEST WILL ACQUIRE " & INTEGER'image(no_of_lines)
                & " LINES" SEVERITY NOTE;
            
            -- Calculate the number of pixels.  Note that the image width is multiplied by 2 in
            -- 16BPP display mode and 4 in 32 BPP mode, then is later divided by the subsample value.
            -- This is stored away as the real number of pixels and used to check the data.
            -- The no_of_pixels that is written to the video fpga must be double-word aligned
            -- and this is calculated next.
            real_no_of_pixels := ((video_timing.hthresh(11) - video_timing.hthresh(6)) * pixmult);

            -- Next we divide by the sub-sample amount and determine
            -- if we need to round up the number of pixels in a scan
            -- line by 1 to form the "real" no of pixels, which is the
            -- number of pixels that we check (not padded by the
            -- hardware):
            IF real_no_of_pixels REM subsample_array(subtest) = 0 THEN
                real_no_of_pixels := real_no_of_pixels/subsample_array(subtest);
            ELSE
                real_no_of_pixels := (real_no_of_pixels/subsample_array(subtest)) + 1;
            END IF;

            -- Next we calculate the no of "programmed" pixels, which are the number of pixels
            -- that we program into the hardware.  The hardware must have them double-work
            -- aligned and will pad out the extra pixels - those between no_of_pixels and
            -- real_no_of_pixels.  We enable padding in the video chips which will
            -- pad out the remaining pixels with the previous value.
            IF real_no_of_pixels REM 8 /= 0 THEN
                no_of_pixels := real_no_of_pixels + 8 - real_no_of_pixels REM 8;
            ELSE
                no_of_pixels := real_no_of_pixels;
            END IF;

            ASSERT FALSE REPORT cr & "  THIS TEST WILL ACQUIRE " & INTEGER'image(no_of_pixels)
                & " PIXELS PER SCAN LINE" SEVERITY NOTE;
            
            ASSERT FALSE REPORT cr & "  THIS TEST WILL CHECK " & INTEGER'image(real_no_of_pixels)
                & " PIXELS PER SCAN LINE" SEVERITY NOTE;
            
            -- set up the image width reg.
            total_pixels := no_of_pixels * no_of_lines;
            data_slv := conv_std_logic_vector(total_pixels, 32) AND x"0000FFF8";
            memwrite(fg_bar1_base + scan0_base + image_width_reg, data_slv, cyc);

            -- set up the image height reg:
            data_slv := conv_std_logic_vector(total_pixels/65536, 32) AND x"000003FF";
            memwrite(fg_bar1_base + scan0_base + image_height_reg, data_slv, cyc);
                
            -- set up the DMA size reg with total pixels:
            memwrite(fg_bar0_base + ch0_base + admasz, total_pixels, cyc);

            -- set up the display control register:
            memwrite(fg_bar1_base + scan0_base + display_control_reg, disp_control_reg, cyc);
            
            -----------------------------------------------------------------------------------
            -- SET THE RUN BITS
            -----------------------------------------------------------------------------------
            -- THIS SECTION SET THE RUN BIT IN THE SCAN AND THE START_EVEN BIT IN THE VIDEO
            -- FPGA TO START THE ACQUISITION.  WE THEN POLL THE EVEN_BUSY BIT UNTIL WE ARE DONE
            -- OR WE TIMEOUT.
            
            -- set up the ADMA cmd reg for block mode DMA and with start even bit set
            memwrite(fg_bar0_base + ch0_base + admacmd,
                     acquire_enable + even + dma_threshold + dma_start_even, cyc);
            
            -- set the state machine in the SCAN
            memwrite(fg_bar1_base + scan0_base + state_mach_reg, 16#0005#, cyc);

            -- set the RUN bit in the SCAN
            memwrite(fg_bar1_base + scan0_base + rev_sync_reg, 16#0040#, cyc);

            -- poll the busy bit until either we are not busy or we time out:
            starttime := NOW;  -- capture time that we start polling
            memread(fg_bar0_base + ch0_base + admast, cyc);
            pause(20, cyc);
            WHILE readdata(0)(even_done_bit) = '0' LOOP
                memread(fg_bar0_base + ch0_base + admast, cyc);
                IF NOW - starttime > timeout THEN
                    ASSERT FALSE REPORT "TIMEOUT IN BASIC TESTMODE ACQ TEST"
                        & cr & "WAITING FOR EVEN_BUSY TO GO INACTIVE (ACQ NEVER COMPLETED)"
                        SEVERITY ERROR;
                    EXIT;
                END IF;
            END LOOP;

            -----------------------------------------------------------------------------------
            -- ACQUISITION COMPLETE
            -----------------------------------------------------------------------------------
            -- WE ARRIVE HERE WHEN THE ACQUISITION COMPLETES.  WE NOW CHECK THE VARIOUS STATUS
            -- REGISTERS TO LOOK FOR HEALTHY STATUS.
            
            REPORT "ACQUISITION COMPLETE (EVEN_BUSY WENT ACTIVE)" SEVERITY NOTE;

            -- check the Acq DMA Status to ensure we ended without errors:
            pause(20, cyc);
            ASSERT (readdata(0) AND healthy_dma_status) = 0
                REPORT "DMA STATUS REGISTER INDICATES "
                & "UNHEALTHY STATUS AT END OF ACQUISITION" SEVERITY ERROR;

            -- check the acquire fifo status:
            memread(fg_bar1_base + scan0_base + acqfifo_status_reg, cyc);
            pause(20, cyc);
            ASSERT (readdata(0) AND healthy_acqfifo_status) = 0
                REPORT "ACQ FIFO STATUS REGISTER INDICATES "
                & "UNHEALTHY STATUS AT END OF ACQUISITION" SEVERITY ERROR;
            
            -- check the even fifo status:
            memread(fg_bar1_base + scan0_base + evenfifo_status_msbreg, cyc);
            pause(20, cyc);
            ASSERT (readdata(0) AND healthy_evenfifo_status) = 0
                REPORT "EVEN FIFO STATUS REGISTER INDICATES "
                & "UNHEALTHY STATUS AT END OF ACQUISITION" SEVERITY ERROR;
            

            
            -- clear the even_done bit in the status register:
            memwrite(fg_bar0_base + ch0_base + admast, (even_done_bit => '1', OTHERS => '0'), cyc);

            -----------------------------------------------------------------------------------
            -- DATA CHECKING
            -----------------------------------------------------------------------------------
            -- HERE WE CHECK THE DATA IN TARGET MEMORY.  WE PERFORM PCI BURST READS IN THE
            -- INNER FOR LOOP TO SPEED UP THE PROCESS.  AT THE END WE GO BACK UP AND DO THE
            -- NEXT SUBTEST IN LINE.
            
            control.subsample := subsample_array(subtest);  -- set up the subsample amount
            addr_offset2 := 0;
            pixgroups := no_of_pixels/4;  -- adjusted number of 4 pix groups/scan line * pix expansion
            real_pixgroups := real_no_of_pixels/4;  -- real number of 4 pix groups/scan line * pix expansion
            FOR lines IN 0 TO no_of_lines-1 LOOP
                ASSERT FALSE REPORT "   ...CHECKING DATA FROM SCAN LINE " & INTEGER'image(lines)
                    SEVERITY NOTE;
                control.reset := TRUE;  -- reset the data pattern at begin of line
                pixel := 0;        -- pixel # in scan line
                WHILE pixel < pixgroups LOOP
                    IF pixel >= real_pixgroups THEN
                        -- when we are at the end of real pixels we increment the address past
                        -- the "padded pixels" - we don't check them
                        pixel := pixel + 1;  -- increment pixel count
                        addr_offset2 := addr_offset2 + 4;     -- increment the address
                    ELSE
                        -- set transfer burst size to the smaller of 14 or the number of pixel
                        -- groups left to check.  The actual burst size will be 0 to 14+1
                        -- because of the burst end operation after the loop:
                        IF real_pixgroups-pixel <= 2 THEN
                            burst_enable := FALSE;
                        ELSIF real_pixgroups-pixel < 16 THEN
                            index_offset := real_pixgroups-pixel-2;
                            burst_enable := TRUE;
                        ELSE
                            burst_enable := TRUE;
                            index_offset := 14;
                        END IF;
                        IF burst_enable THEN
                            FOR i IN 0 TO index_offset LOOP
                                ASSERT NOT debug REPORT "starting pixel group " & INTEGER'image(pixel)
                                    SEVERITY NOTE;
                                getcheckdata(data_ptn, control, data_slv, seed);
                                membread(tar_data_base + addr_offset2, data_slv, cyc);
                                pixel := pixel + 1;  -- increment pixel count
                                addr_offset2 := addr_offset2 + 4;     -- increment the address
                                control.reset := FALSE;
                            END LOOP;
                        END IF;
                        ASSERT NOT debug REPORT "starting pixel group " & INTEGER'image(pixel)
                            SEVERITY NOTE;
                        getcheckdata(data_ptn, control, data_slv, seed);
                        membreadend(tar_data_base + addr_offset2, data_slv, cyc);
                        control.reset := FALSE;
                        pixel := pixel + 1;  -- increment pixel count
                        addr_offset2 := addr_offset2 + 4;     -- increment the address
                    END IF;
                    
                END LOOP;
            END LOOP;

            
            ASSERT NOT (subtest_list(subtest) = basic_acq)
                REPORT "BASIC TEST MODE ACQ FINISHED" SEVERITY NOTE;
            ASSERT NOT (subtest_list(subtest) = display_format_16bpp)
                REPORT "16BPP DISPLAY FORMAT ACQ TEST FINISHED" SEVERITY NOTE;
            ASSERT NOT (subtest_list(subtest) = display_format_32bpp)
                REPORT "32BPP DISPLAY FORMAT ACQ TEST FINISHED" SEVERITY NOTE;
            pause(10, cyc);
        
        
        END LOOP;  -- loop for subtest_list

        pause(20, cyc);
        stop(cyc);
            
END initcycs;

END pcicycs;