-------------------------------------------------------------------------------
-- Title         : OSRAM SCDV5540 Display Controller
-- Project       : General Purpose Core
-------------------------------------------------------------------------------
-- File          : DisplayControl.vhd
-- Author        : Ryan Herbst, rherbst@slac.stanford.edu
-- Created       : 12/06/2007
-------------------------------------------------------------------------------
-- Description:
-- Source code for display controller for OSRAM SCDV5540 4-digit LED
-- display. An 8-bit input value is input for each of the 4 digits on the 
-- LED display. This 8-bit value is used to lookup a charactor from a defined
-- table of charactors. 
-- See DisplayCharacters file for character definitions.
-------------------------------------------------------------------------------
-- Copyright (c) 2007 by Ryan Herbst. All rights reserved.
-------------------------------------------------------------------------------
-- Modification history:
-- 12/06/2007: created.
-- 03/06/2008: Fixed reset of shiftValue
-------------------------------------------------------------------------------
library ieee;
library Unisim;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;

library hsio;
use hsio.disp_characters.all;

entity disp_control is
  port (

    -- Master Clock & reset
    sysClk : in std_logic;
    sysRst : in std_logic;

    -- Display timing strobe. 200ns min period.
    dispStrobe : in std_logic;

    -- Update display 
    dispUpdate : in std_logic;

    -- Display rotation, 0=0, 1=90, 2=180, 3=270
    dispRotate : in std_logic_vector(1 downto 0);

    -- Index value for charactor digits
    dispDigitA : in std_logic_vector(7 downto 0);
    dispDigitB : in std_logic_vector(7 downto 0);
    dispDigitC : in std_logic_vector(7 downto 0);
    dispDigitD : in std_logic_vector(7 downto 0);

    -- Outputs to display device
    dispClk   : out std_logic;
    dispDat   : out std_logic;
    dispLoadL : out std_logic;
    dispRstL  : out std_logic
    );

  -- Keep from combinging output clocks
  attribute syn_preserve            : boolean;
  attribute syn_preserve of dispClk : signal is true;

end disp_control;


-- Define architecture for first level module
architecture rtl of disp_control is

  -- Local signals
  signal intLoad     : std_logic;
  signal intData     : std_logic;
  signal charCount   : std_logic_vector(1 downto 0);
  signal wordCount   : std_logic_vector(2 downto 0);
  signal bitCount    : std_logic_vector(3 downto 0);
  signal shiftCount  : std_logic_vector(6 downto 0);
  signal shiftValue  : std_logic_vector(63 downto 0);  --*** was 39, but -modelsim said no
  signal newShift    : std_logic_vector(63 downto 0);  --*** ditto
  signal lookupValue : std_logic_vector(24 downto 0);
  signal newDigit    : std_logic_vector(7 downto 0);
  signal strobeDelay : std_logic;
  signal strobeEdge  : std_logic;
  signal shiftEn     : std_logic;

  -- Keep from combinging output clocks
  attribute syn_preserve of strobeDelay : signal is true;

  -- Register delay for simulation
  constant tpd : time := 0.5 ns;

begin

  -- Output Control Lines
  process (sysClk)
  begin
    if rising_edge(sysClk) then
      if (sysRst = '1') then
        dispLoadL <= '1' after tpd;
        dispRstL  <= '0' after tpd;
        dispClk   <= '0' after tpd;
        dispDat   <= '0' after tpd;

      else
        dispLoadL <= not intLoad    after tpd;
        dispRstL  <= '1'            after tpd;
        dispClk   <= not dispStrobe after tpd;
        dispDat   <= intData        after tpd;

      end if;
    end if;
  end process;


  -- Strobe edge detection
  strobeEdge <= dispStrobe and not strobeDelay;

  -- Shift sequence control and counters
  process (sysClk)
  begin
    if rising_edge(sysClk) then
      if (sysRst = '1') then
        charCount   <= (others => '0') after tpd;
        wordCount   <= (others => '0') after tpd;
        shiftCount  <= (others => '0') after tpd;
        bitCount    <= (others => '0') after tpd;
        shiftEn     <= '0'             after tpd;
        intLoad     <= '0'             after tpd;
        intData     <= '0'             after tpd;
        strobeDelay <= '0'             after tpd;
        shiftValue  <= (others => '0') after tpd;

      else
                                        -- Delayed copy of shift
        strobeDelay <= dispStrobe after tpd;

                                        -- Sequnce is not running
        if shiftEn = '0' then
          charCount  <= (others => '0') after tpd;
          wordCount  <= (others => '0') after tpd;
          shiftCount <= (others => '0') after tpd;
          bitCount   <= (others => '0') after tpd;
          shiftEn    <= dispUpdate      after tpd;
          intLoad    <= '0'             after tpd;
          intData    <= '0'             after tpd;

                                        -- Sequence Is Running
        elsif strobeEdge = '1' then

                                        -- Bit Counter For 8-bit word plus 3-bit space
          if bitCount = 12 then
            bitCount <= (others => '0') after tpd;
          else
            bitCount <= bitCount + 1    after tpd;
          end if;

                                        -- Word Counter, Digit address, followed by 4 Rows
          if bitCount = 12 then
            if wordCount = 5 then
              wordCount <= (others => '0') after tpd;
            else
              wordCount <= wordCount + 1   after tpd;
            end if;
          end if;

                                        -- Char Counter, 4 Characters
          if bitCount = 12 and wordCount = 5 then
            if charCount = 3 then
              charCount <= (others => '0') after tpd;
              shiftEn   <= '0'             after tpd;
            else
              charCount <= charCount + 1   after tpd;
            end if;
          end if;

                                        -- Shift Bit Counter, Shift Output for words 1-5
          if wordCount = 0 then
            shiftCount <= (others => '0') after tpd;
            shiftValue <= newShift        after tpd;
          elsif bitCount < 8 then
            shiftCount <= shiftCount + 1  after tpd;
          end if;

                                        -- Character address is first word
          if wordCount = 0 then
            case bitCount is
              when "0000" => intData <= charCount(0) after tpd;
              when "0001" => intData <= charCount(1) after tpd;
              when "0010" => intData <= '0'          after tpd;
              when "0011" => intData <= '0'          after tpd;
              when "0100" => intData <= '0'          after tpd;
              when "0101" => intData <= '1'          after tpd;
              when "0110" => intData <= '0'          after tpd;
              when "0111" => intData <= '1'          after tpd;
              when others => intData <= '0'          after tpd;
            end case;

                                        -- Shift out digit values for words 1-5
          else
            intData <= shiftValue(conv_integer(shiftCount)) after tpd;
          end if;

                                        -- Load Strobe asserted for bits 0-7
          if bitCount < 8 then
            intLoad <= '1' after tpd;
          else
            intLoad <= '0' after tpd;
          end if;
        end if;
      end if;
    end if;
  end process;


                                        -- Select Digit For Display
  newDigit <= dispDigitA when charCount = 0 else
              dispDigitB when charCount = 1 else
              dispDigitC when charCount = 2 else
              dispDigitD;

                                        -- Get Raw Lookup Value
  lookupValue <= (others => '0') when newDigit > DISPMAX else DISPLOOKUP(conv_integer(newDigit));

                                        -- Determine rotation of display digit and add row addresses
  newShift <=

                                        -- No Rotation
    "000000000000000000000000" &        --***
    "000" & lookupValue(24 downto 20) &
    "001" & lookupValue(19 downto 15) &
    "010" & lookupValue(14 downto 10) &
    "011" & lookupValue(9 downto 5) &
    "100" & lookupValue(4 downto 0) when dispRotate = 0 else

                                        -- 90 Deg Rotation
    "000000000000000000000000" &        --***
    "000" & lookupValue(4) & lookupValue(9) &
    lookupValue(14) & lookupValue(19) & lookupValue(24) &
    "001" & lookupValue(3) & lookupValue(8) &
    lookupValue(13) & lookupValue(18) & lookupValue(23) &
    "010" & lookupValue(2) & lookupValue(7) &
    lookupValue(12) & lookupValue(17) & lookupValue(22) &
    "011" & lookupValue(1) & lookupValue(6) &
    lookupValue(11) & lookupValue(16) & lookupValue(21) &
    "100" & lookupValue(0) & lookupValue(5) &
    lookupValue(10) & lookupValue(15) & lookupValue(20) when dispRotate = 1 else

                                        -- 180 Deg Rotation
    "000000000000000000000000" &        --***
    "000" & lookupValue(0) & lookupValue(1) &
    lookupValue(2) & lookupValue(3) & lookupValue(4) &
    "001" & lookupValue(5) & lookupValue(6) &
    lookupValue(7) & lookupValue(8) & lookupValue(9) &
    "010" & lookupValue(10) & lookupValue(11) &
    lookupValue(12) & lookupValue(13) & lookupValue(14) &
    "011" & lookupValue(15) & lookupValue(16) &
    lookupValue(17) & lookupValue(18) & lookupValue(19) &
    "100" & lookupValue(20) & lookupValue(21) &
    lookupValue(22) & lookupValue(23) & lookupValue(24) when dispRotate = 2 else

                                        -- 270 Deg Rotation
    "000000000000000000000000" &        --***
    "000" & lookupValue(20) & lookupValue(15) &
    lookupValue(10) & lookupValue(5) & lookupValue(0) &
    "001" & lookupValue(21) & lookupValue(16) &
    lookupValue(11) & lookupValue(6) & lookupValue(1) &
    "010" & lookupValue(22) & lookupValue(17) &
    lookupValue(12) & lookupValue(7) & lookupValue(2) &
    "011" & lookupValue(23) & lookupValue(18) &
    lookupValue(13) & lookupValue(8) & lookupValue(3) &
    "100" & lookupValue(24) & lookupValue(19) &
    lookupValue(14) & lookupValue(9) & lookupValue(4);

end rtl;