OpenCores
URL https://opencores.org/ocsvn/irig_regenerator/irig_regenerator/trunk

Subversion Repositories irig_regenerator

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /
    from Rev 1 to Rev 2
    Reverse comparison

Rev 1 → Rev 2

/irig_regenerator/trunk/rtl/convert_pack.vhd
0,0 → 1,1469
-- A package containing various conversion functions useful in testbenches,
-- especially when used with text file IO in reading and displaying
-- hexadecimal values.
--
-- Author : Bill Grigsby
-- Modifications by : John Clayton
--
 
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
use std.textio.all;
 
package convert_pack is
 
------------------------------------------------------------------------------------
-- function calls
------------------------------------------------------------------------------------
function string_to_integer (in_string : string) return integer ;
function vector_to_string (in_vector : std_logic_vector) return string ;
function char_to_bit (in_char : character) return std_logic ;
function char_to_hex (in_char : character) return std_logic_vector ;
 
function slv2string(in_vector : std_logic_vector; nibbles : natural) return string ; -- Name changed by John Clayton
function u2string(in_vector : unsigned; nibbles : natural) return string ; -- Added by John Clayton
function u2asciichar(in_vector : unsigned(7 downto 0)) return character ; -- Added by John Clayton
function asciichar2u(in_char : character) return unsigned; -- Added by John Clayton
 
function hex_to_ascii(in_vector : std_logic_vector; nibbles : natural) return string ;
function u2ascii(in_vector : unsigned; nibbles : natural) return string ; -- Added by John Clayton
function hex_data_wrd(in_vector : std_logic_vector) return string ;
function hex_data_dblwrd(in_vector : std_logic_vector) return string ;
function hex_data_dblwrdz(in_vector : std_logic_vector) return string ;
 
function is_hex(c : character) return boolean; -- Added by John Clayton
function is_std_logic(c : character) return boolean; -- Added by John Clayton
function is_space(c : character) return boolean; -- Added by John Clayton
function char2sl(in_char : character) return std_logic; -- Added by John Clayton
function char2slv(in_char : character) return std_logic_vector;
function char2u(in_char : character) return unsigned;
function slv2u(in_a : std_logic_vector) return unsigned; -- Added by John Clayton
function u2slv(in_a : unsigned) return std_logic_vector; -- Added by John Clayton
function slv2s(in_a : std_logic_vector) return signed; -- Added by John Clayton
function s2slv(in_a : signed) return std_logic_vector; -- Added by John Clayton
function str2u(in_string : string; out_size:integer) return unsigned; -- Added by John Clayton
function str2s(in_string : string; out_size:integer) return signed; -- Added by John Clayton
-- function "**"(in_a : natural; in_b : natural) return natural; -- Added by John Clayton
function pow_2_u(in_a : natural; out_size:integer) return unsigned; -- Added by John Clayton
function asr_function(in_vect : signed; in_a : natural) return signed; -- Added by John Clayton
 
function slv_resize(in_vect : std_logic_vector; out_size : integer) return std_logic_vector; -- Added by John Clayton
function slv_resize_l(in_vect : std_logic_vector; out_size : integer) return std_logic_vector; -- Added by John Clayton
function slv_resize_se(in_vect : std_logic_vector; out_size : integer) return std_logic_vector; -- Added by John Clayton
function s_resize(in_vect : signed; out_size : integer) return signed; -- Added by John Clayton
function s_resize_l(in_vect : signed; out_size : integer) return signed; -- Added by John Clayton
function s_resize_se(in_vect : signed; out_size : integer) return signed; -- Added by John Clayton
function u_resize(in_vect : unsigned; out_size : integer) return unsigned; -- Added by John Clayton
function u_resize_l(in_vect : unsigned; out_size : integer) return unsigned; -- Added by John Clayton
function u_select(in_vect : unsigned; slice_num : integer; slice_size : integer) return unsigned; -- Added by John Clayton
function u_reverse(in_vect : unsigned) return unsigned; -- Added by John Clayton
 
function u_recursive_parity ( x : unsigned ) return std_logic; -- Added by John Clayton
 
function bit_width (maxval : integer) return integer ; -- Added by John Clayton
function bit_width (maxval : real) return integer ; -- Added by John Clayton
function timer_width (maxval : integer) return integer ; -- Added by John Clayton
function timer_width (maxval : real) return integer ; -- Added by John Clayton
function num_words (num_bits : integer; bits_per_word : integer) return integer ; -- Added by Philip Kasavan
function asciichar2u2(d:character) return unsigned; -- Added by John Clayton. Loosely based on code in Thesis by Rudi Rughoonundon, 11-1-1996
function str2u(s: string) return unsigned; -- Added by John Clayton, Converts a string of ASCII characters into unsigned
function u_ones(in_a : unsigned) return natural; -- Added by John Clayton, counts the number of '1' bits in an unsigned
 
 
------------------------------------------------------------------------------------
-- procedures
------------------------------------------------------------------------------------
 
 
end convert_pack;
 
 
package body convert_pack is
 
------------------------------------------------------------------------------------
--
------------------------------------------------------------------------------------
--* Title : TEST_PARITY
--* Filename & Ext : test_parity.vhdl
--* Author : David Bishop X-66788
--* Created : 3/18/97
--* Version : 1.2
--* Revision Date : 97/04/15
--* SCCSid : 1.2 04/15/97 test_parity.vhdl
--* WORK Library : testchip
--* Mod History :
--* Description : This is a parity generator which is written recursively
--* : It is designed to test the ability of Simulation and
--* : Synthesis tools to check this capability.
--* Known Bugs :
--* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 
 
function u_recursive_parity ( x : unsigned ) return std_logic is
variable Upper, Lower : std_logic;
variable Half : integer;
variable BUS_int : unsigned( x'length-1 downto 0 );
variable Result : std_logic;
begin
BUS_int := x;
if ( BUS_int'length = 1 ) then
Result := BUS_int ( BUS_int'left );
elsif ( BUS_int'length = 2 ) then
Result := BUS_int ( BUS_int'right ) xor BUS_int ( BUS_int'left );
else
Half := ( BUS_int'length + 1 ) / 2 + BUS_int'right;
Upper := u_recursive_parity ( BUS_int ( BUS_int'left downto Half ));
Lower := u_recursive_parity ( BUS_int ( Half - 1 downto BUS_int'right ));
Result := Upper xor Lower;
end if;
return Result;
end;
 
------------------------------------------------------------------------------------
--
------------------------------------------------------------------------------------
 
function vector_to_string (in_vector : std_logic_vector) return string is
variable out_string : string(32 downto 1);
begin
for i in in_vector'range loop
if in_vector(i) = '1' then
out_string(i+1) := '1';
elsif in_vector(i) = '0' then
out_string(i+1) := '0';
else
assert false
report " illegal bit vector to bit string"
severity note;
end if;
end loop;
return out_string;
 
end vector_to_string;
 
------------------------------------------------------------------------------------
--
------------------------------------------------------------------------------------
 
function string_to_integer (in_string : string) return integer is
 
variable int : integer := 0;
begin
for j in in_string'range loop
case in_string(j) is
when '0' => int := int;
when '1' => int := int + (1 * 10**(j-1));
when '2' => int := int + (2 * 10**(j-1));
when '3' => int := int + (3 * 10**(j-1));
when '4' => int := int + (4 * 10**(j-1));
when '5' => int := int + (5 * 10**(j-1));
when '6' => int := int + (6 * 10**(j-1));
when '7' => int := int + (7 * 10**(j-1));
when '8' => int := int + (8 * 10**(j-1));
when '9' => int := int + (9 * 10**(j-1));
when others =>
assert false
report " illegal character to integer"
severity note;
end case;
end loop;
return int;
end string_to_integer;
 
------------------------------------------------------------------------------------
--
------------------------------------------------------------------------------------
 
function char_to_bit (in_char : character) return std_logic is
variable out_bit : std_logic;
begin
if (in_char = '1') then
out_bit := '1';
elsif (in_char = '0') then
out_bit := '0';
else
assert false
report "illegal character to bit"
severity note;
end if;
return out_bit;
end char_to_bit;
 
------------------------------------------------------------------------------------
--
------------------------------------------------------------------------------------
function char_to_hex (in_char : character) return std_logic_vector is
 
variable out_vec : std_logic_vector(3 downto 0);
 
begin
case in_char is
when '0' => out_vec := "0000";
when '1' => out_vec := "0001";
when '2' => out_vec := "0010";
when '3' => out_vec := "0011";
when '4' => out_vec := "0100";
when '5' => out_vec := "0101";
when '6' => out_vec := "0110";
when '7' => out_vec := "0111";
when '8' => out_vec := "1000";
when '9' => out_vec := "1001";
when 'A' | 'a' => out_vec := "1010";
when 'B' | 'b' => out_vec := "1011";
when 'C' | 'c' => out_vec := "1100";
when 'D' | 'd' => out_vec := "1101";
when 'E' | 'e' => out_vec := "1110";
when 'F' | 'f' => out_vec := "1111";
when others =>
assert false
report " illegal character to hex"
severity note;
end case;
return out_vec;
end char_to_hex;
 
------------------------------------------------------------------------------------
--
------------------------------------------------------------------------------------
 
function slv2string(in_vector : std_logic_vector; nibbles : natural) return string is
variable out_string : string(1 to nibbles);
variable temp_in_vector : std_logic_vector(4*nibbles-1 downto 0);
variable vector : std_logic_vector(3 downto 0);
begin
temp_in_vector := in_vector(in_vector'length-1 downto in_vector'length-temp_in_vector'length);
for i in 1 to nibbles loop
vector := temp_in_vector((4*(nibbles-i)+3) downto 4*(nibbles-i));
case vector is
when "0000" => out_string(i) := '0';
when "0001" => out_string(i) := '1';
when "0010" => out_string(i) := '2';
when "0011" => out_string(i) := '3';
when "0100" => out_string(i) := '4';
when "0101" => out_string(i) := '5';
when "0110" => out_string(i) := '6';
when "0111" => out_string(i) := '7';
when "1000" => out_string(i) := '8';
when "1001" => out_string(i) := '9';
when "1010" => out_string(i) := 'A';
when "1011" => out_string(i) := 'B';
when "1100" => out_string(i) := 'C';
when "1101" => out_string(i) := 'D';
when "1110" => out_string(i) := 'E';
when "1111" => out_string(i) := 'F';
when others =>
out_string(i) := 'J';
assert false
report " illegal std_logic_vector to string"
severity note;
end case;
end loop;
return out_string;
end;
 
------------------------------------------------------------------------------------
--
------------------------------------------------------------------------------------
 
function u2string(in_vector : unsigned; nibbles : natural) return string is
variable out_string : string(1 to nibbles);
variable temp_in_vector : unsigned(4*nibbles-1 downto 0);
variable vector : unsigned(3 downto 0);
begin
temp_in_vector := in_vector(in_vector'length-1 downto in_vector'length-temp_in_vector'length);
for i in 1 to nibbles loop
vector := temp_in_vector((4*(nibbles-i)+3) downto 4*(nibbles-i));
case vector is
when "0000" => out_string(i) := '0';
when "0001" => out_string(i) := '1';
when "0010" => out_string(i) := '2';
when "0011" => out_string(i) := '3';
when "0100" => out_string(i) := '4';
when "0101" => out_string(i) := '5';
when "0110" => out_string(i) := '6';
when "0111" => out_string(i) := '7';
when "1000" => out_string(i) := '8';
when "1001" => out_string(i) := '9';
when "1010" => out_string(i) := 'A';
when "1011" => out_string(i) := 'B';
when "1100" => out_string(i) := 'C';
when "1101" => out_string(i) := 'D';
when "1110" => out_string(i) := 'E';
when "1111" => out_string(i) := 'F';
when others =>
out_string(i) := 'U';
assert false report " illegal unsigned to string" severity note;
end case;
end loop;
return out_string;
end;
 
------------------------------------------------------------------------------------
--
------------------------------------------------------------------------------------
 
function u2asciichar(in_vector : unsigned(7 downto 0)) return character is
variable out_char : character;
begin
case in_vector is
when "00001001" => out_char := HT; -- Horizontal Tab
when "00100000" => out_char := ' ';
when "00100001" => out_char := '!';
when "00100010" => out_char := '"';
when "00100011" => out_char := '#';
when "00100100" => out_char := '$';
when "00100101" => out_char := '%';
when "00100110" => out_char := '&';
when "00100111" => out_char := ''';
when "00101000" => out_char := '(';
when "00101001" => out_char := ')';
when "00101010" => out_char := '*';
when "00101011" => out_char := '+';
when "00101100" => out_char := ',';
when "00101101" => out_char := '-';
when "00101110" => out_char := '.';
when "00101111" => out_char := '/';
when "00110000" => out_char := '0';
when "00110001" => out_char := '1';
when "00110010" => out_char := '2';
when "00110011" => out_char := '3';
when "00110100" => out_char := '4';
when "00110101" => out_char := '5';
when "00110110" => out_char := '6';
when "00110111" => out_char := '7';
when "00111000" => out_char := '8';
when "00111001" => out_char := '9';
when "00111010" => out_char := ':';
when "00111011" => out_char := ';';
when "00111100" => out_char := '<';
when "00111101" => out_char := '=';
when "00111110" => out_char := '>';
when "00111111" => out_char := '?';
when "01000000" => out_char := '@';
when "01000001" => out_char := 'A';
when "01000010" => out_char := 'B';
when "01000011" => out_char := 'C';
when "01000100" => out_char := 'D';
when "01000101" => out_char := 'E';
when "01000110" => out_char := 'F';
when "01000111" => out_char := 'G';
when "01001000" => out_char := 'H';
when "01001001" => out_char := 'I';
when "01001010" => out_char := 'J';
when "01001011" => out_char := 'K';
when "01001100" => out_char := 'L';
when "01001101" => out_char := 'M';
when "01001110" => out_char := 'N';
when "01001111" => out_char := 'O';
when "01010000" => out_char := 'P';
when "01010001" => out_char := 'Q';
when "01010010" => out_char := 'R';
when "01010011" => out_char := 'S';
when "01010100" => out_char := 'T';
when "01010101" => out_char := 'U';
when "01010110" => out_char := 'V';
when "01010111" => out_char := 'W';
when "01011000" => out_char := 'X';
when "01011001" => out_char := 'Y';
when "01011010" => out_char := 'Z';
when "01011011" => out_char := '[';
when "01011100" => out_char := '\';
when "01011101" => out_char := ']';
when "01011110" => out_char := '^';
when "01011111" => out_char := '_';
when "01100000" => out_char := '`';
when "01100001" => out_char := 'a';
when "01100010" => out_char := 'b';
when "01100011" => out_char := 'c';
when "01100100" => out_char := 'd';
when "01100101" => out_char := 'e';
when "01100110" => out_char := 'f';
when "01100111" => out_char := 'g';
when "01101000" => out_char := 'h';
when "01101001" => out_char := 'i';
when "01101010" => out_char := 'j';
when "01101011" => out_char := 'k';
when "01101100" => out_char := 'l';
when "01101101" => out_char := 'm';
when "01101110" => out_char := 'n';
when "01101111" => out_char := 'o';
when "01110000" => out_char := 'p';
when "01110001" => out_char := 'q';
when "01110010" => out_char := 'r';
when "01110011" => out_char := 's';
when "01110100" => out_char := 't';
when "01110101" => out_char := 'u';
when "01110110" => out_char := 'v';
when "01110111" => out_char := 'w';
when "01111000" => out_char := 'x';
when "01111001" => out_char := 'y';
when "01111010" => out_char := 'z';
when "01111011" => out_char := '{';
when "01111100" => out_char := '|';
when "01111101" => out_char := '}';
when "01111110" => out_char := '~';
when others =>
out_char := '*';
--assert false report " illegal unsigned to ascii character" severity note;
end case;
return out_char;
end;
 
------------------------------------------------------------------------------------
--
------------------------------------------------------------------------------------
 
function asciichar2u(in_char : character) return unsigned is
variable out_vect : unsigned(7 downto 0);
begin
case in_char is
when HT => out_vect := "00001001";
when ' ' => out_vect := "00100000";
when '!' => out_vect := "00100001";
when '"' => out_vect := "00100010";
when '#' => out_vect := "00100011";
when '$' => out_vect := "00100100";
when '%' => out_vect := "00100101";
when '&' => out_vect := "00100110";
when ''' => out_vect := "00100111";
when '(' => out_vect := "00101000";
when ')' => out_vect := "00101001";
when '*' => out_vect := "00101010";
when '+' => out_vect := "00101011";
when ',' => out_vect := "00101100";
when '-' => out_vect := "00101101";
when '.' => out_vect := "00101110";
when '/' => out_vect := "00101111";
when '0' => out_vect := "00110000";
when '1' => out_vect := "00110001";
when '2' => out_vect := "00110010";
when '3' => out_vect := "00110011";
when '4' => out_vect := "00110100";
when '5' => out_vect := "00110101";
when '6' => out_vect := "00110110";
when '7' => out_vect := "00110111";
when '8' => out_vect := "00111000";
when '9' => out_vect := "00111001";
when ':' => out_vect := "00111010";
when ';' => out_vect := "00111011";
when '<' => out_vect := "00111100";
when '=' => out_vect := "00111101";
when '>' => out_vect := "00111110";
when '?' => out_vect := "00111111";
when '@' => out_vect := "01000000";
when 'A' => out_vect := "01000001";
when 'B' => out_vect := "01000010";
when 'C' => out_vect := "01000011";
when 'D' => out_vect := "01000100";
when 'E' => out_vect := "01000101";
when 'F' => out_vect := "01000110";
when 'G' => out_vect := "01000111";
when 'H' => out_vect := "01001000";
when 'I' => out_vect := "01001001";
when 'J' => out_vect := "01001010";
when 'K' => out_vect := "01001011";
when 'L' => out_vect := "01001100";
when 'M' => out_vect := "01001101";
when 'N' => out_vect := "01001110";
when 'O' => out_vect := "01001111";
when 'P' => out_vect := "01010000";
when 'Q' => out_vect := "01010001";
when 'R' => out_vect := "01010010";
when 'S' => out_vect := "01010011";
when 'T' => out_vect := "01010100";
when 'U' => out_vect := "01010101";
when 'V' => out_vect := "01010110";
when 'W' => out_vect := "01010111";
when 'X' => out_vect := "01011000";
when 'Y' => out_vect := "01011001";
when 'Z' => out_vect := "01011010";
when '[' => out_vect := "01011011";
when '\' => out_vect := "01011100";
when ']' => out_vect := "01011101";
when '^' => out_vect := "01011110";
when '_' => out_vect := "01011111";
when '`' => out_vect := "01100000";
when 'a' => out_vect := "01100001";
when 'b' => out_vect := "01100010";
when 'c' => out_vect := "01100011";
when 'd' => out_vect := "01100100";
when 'e' => out_vect := "01100101";
when 'f' => out_vect := "01100110";
when 'g' => out_vect := "01100111";
when 'h' => out_vect := "01101000";
when 'i' => out_vect := "01101001";
when 'j' => out_vect := "01101010";
when 'k' => out_vect := "01101011";
when 'l' => out_vect := "01101100";
when 'm' => out_vect := "01101101";
when 'n' => out_vect := "01101110";
when 'o' => out_vect := "01101111";
when 'p' => out_vect := "01110000";
when 'q' => out_vect := "01110001";
when 'r' => out_vect := "01110010";
when 's' => out_vect := "01110011";
when 't' => out_vect := "01110100";
when 'u' => out_vect := "01110101";
when 'v' => out_vect := "01110110";
when 'w' => out_vect := "01110111";
when 'x' => out_vect := "01111000";
when 'y' => out_vect := "01111001";
when 'z' => out_vect := "01111010";
when '{' => out_vect := "01111011";
when '|' => out_vect := "01111100";
when '}' => out_vect := "01111101";
when '~' => out_vect := "01111110";
when others =>
out_vect := "00101010";
--assert false report " illegal char to unsigned" severity note;
end case;
return out_vect;
end;
 
------------------------------------------------------------------------------------
--
------------------------------------------------------------------------------------
function char2slv (in_char : character) return std_logic_vector is
 
variable out_vec : std_logic_vector(3 downto 0);
begin
case in_char is
when '0' => out_vec := "0000";
when '1' => out_vec := "0001";
when '2' => out_vec := "0010";
when '3' => out_vec := "0011";
when '4' => out_vec := "0100";
when '5' => out_vec := "0101";
when '6' => out_vec := "0110";
when '7' => out_vec := "0111";
when '8' => out_vec := "1000";
when '9' => out_vec := "1001";
when 'A' | 'a' => out_vec := "1010";
when 'B' | 'b' => out_vec := "1011";
when 'C' | 'c' => out_vec := "1100";
when 'D' | 'd' => out_vec := "1101";
when 'E' | 'e' => out_vec := "1110";
when 'F' | 'f' => out_vec := "1111";
when others =>
out_vec := "0000";
end case;
return out_vec;
end char2slv;
 
------------------------------------------------------------------------------------
--
------------------------------------------------------------------------------------
function char2u(in_char : character) return unsigned is
 
variable out_vec : unsigned(3 downto 0);
begin
case in_char is
when '0' => out_vec := "0000";
when '1' => out_vec := "0001";
when '2' => out_vec := "0010";
when '3' => out_vec := "0011";
when '4' => out_vec := "0100";
when '5' => out_vec := "0101";
when '6' => out_vec := "0110";
when '7' => out_vec := "0111";
when '8' => out_vec := "1000";
when '9' => out_vec := "1001";
when 'A' | 'a' => out_vec := "1010";
when 'B' | 'b' => out_vec := "1011";
when 'C' | 'c' => out_vec := "1100";
when 'D' | 'd' => out_vec := "1101";
when 'E' | 'e' => out_vec := "1110";
when 'F' | 'f' => out_vec := "1111";
when others =>
out_vec := "0000";
end case;
return out_vec;
end char2u;
 
------------------------------------------------------------------------------------
--
------------------------------------------------------------------------------------
 
function hex_to_ascii(in_vector : std_logic_vector; nibbles : natural) return string is
variable out_string : string(1 to nibbles);
variable temp_in_vector : std_logic_vector(4*nibbles-1 downto 0);
variable vector : std_logic_vector(3 downto 0);
begin
temp_in_vector := in_vector(in_vector'length-1 downto in_vector'length-temp_in_vector'length);
for i in 1 to nibbles loop
vector := temp_in_vector((4*(nibbles-i)+3) downto 4*(nibbles-i));
case vector is
when "0000" => out_string(i) := '0';
when "0001" => out_string(i) := '1';
when "0010" => out_string(i) := '2';
when "0011" => out_string(i) := '3';
when "0100" => out_string(i) := '4';
when "0101" => out_string(i) := '5';
when "0110" => out_string(i) := '6';
when "0111" => out_string(i) := '7';
when "1000" => out_string(i) := '8';
when "1001" => out_string(i) := '9';
when "1010" => out_string(i) := 'A';
when "1011" => out_string(i) := 'B';
when "1100" => out_string(i) := 'C';
when "1101" => out_string(i) := 'D';
when "1110" => out_string(i) := 'E';
when "1111" => out_string(i) := 'F';
when others =>
out_string(i) := 'J';
assert false
report " illegal std_logic_vector to string"
severity note;
end case;
end loop;
return out_string;
end;
 
------------------------------------------------------------------------------------
--
------------------------------------------------------------------------------------
 
function u2ascii(in_vector : unsigned; nibbles : natural) return string is
variable out_string : string(1 to nibbles);
variable temp_in_vector : unsigned(4*nibbles-1 downto 0);
variable vector : unsigned(3 downto 0);
begin
temp_in_vector := in_vector(in_vector'length-1 downto in_vector'length-temp_in_vector'length);
for i in 1 to nibbles loop
vector := temp_in_vector((4*(nibbles-i)+3) downto 4*(nibbles-i));
case vector is
when "0000" => out_string(i) := '0';
when "0001" => out_string(i) := '1';
when "0010" => out_string(i) := '2';
when "0011" => out_string(i) := '3';
when "0100" => out_string(i) := '4';
when "0101" => out_string(i) := '5';
when "0110" => out_string(i) := '6';
when "0111" => out_string(i) := '7';
when "1000" => out_string(i) := '8';
when "1001" => out_string(i) := '9';
when "1010" => out_string(i) := 'A';
when "1011" => out_string(i) := 'B';
when "1100" => out_string(i) := 'C';
when "1101" => out_string(i) := 'D';
when "1110" => out_string(i) := 'E';
when "1111" => out_string(i) := 'F';
when others =>
out_string(i) := 'U';
assert false report " illegal unsigned to string" severity note;
end case;
end loop;
return out_string;
end;
 
------------------------------------------------------------------------------------
--
------------------------------------------------------------------------------------
 
function hex_data_wrd(in_vector : std_logic_vector) return string is
variable out_string : string(1 to 8);
variable temp_in_vector : std_logic_vector(31 downto 0);
variable vector : std_logic_vector(3 downto 0);
begin
temp_in_vector := in_vector;
for i in 1 to 8 loop
vector := temp_in_vector((35-(4*i)) downto (32-(4*i)));
case vector is
when "0000" => out_string(i) := '0';
when "0001" => out_string(i) := '1';
when "0010" => out_string(i) := '2';
when "0011" => out_string(i) := '3';
when "0100" => out_string(i) := '4';
when "0101" => out_string(i) := '5';
when "0110" => out_string(i) := '6';
when "0111" => out_string(i) := '7';
when "1000" => out_string(i) := '8';
when "1001" => out_string(i) := '9';
when "1010" => out_string(i) := 'A';
when "1011" => out_string(i) := 'B';
when "1100" => out_string(i) := 'C';
when "1101" => out_string(i) := 'D';
when "1110" => out_string(i) := 'E';
when "1111" => out_string(i) := 'F';
when others =>
out_string(i) := 'J';
assert false
report " illegal std_logic_vector to string"
severity note;
end case;
end loop;
return out_string;
end;
 
------------------------------------------------------------------------------------
--
------------------------------------------------------------------------------------
 
function hex_data_dblwrd(in_vector : std_logic_vector) return string is
variable out_string : string(1 to 16);
variable temp_in_vector : std_logic_vector(63 downto 0);
variable vector : std_logic_vector(3 downto 0);
begin
temp_in_vector := in_vector;
for i in 1 to 16 loop
vector := temp_in_vector((67-(4*i)) downto (64-(4*i)));
case vector is
when "0000" => out_string(i) := '0';
when "0001" => out_string(i) := '1';
when "0010" => out_string(i) := '2';
when "0011" => out_string(i) := '3';
when "0100" => out_string(i) := '4';
when "0101" => out_string(i) := '5';
when "0110" => out_string(i) := '6';
when "0111" => out_string(i) := '7';
when "1000" => out_string(i) := '8';
when "1001" => out_string(i) := '9';
when "1010" => out_string(i) := 'A';
when "1011" => out_string(i) := 'B';
when "1100" => out_string(i) := 'C';
when "1101" => out_string(i) := 'D';
when "1110" => out_string(i) := 'E';
when "1111" => out_string(i) := 'F';
when others =>
out_string(i) := 'J';
assert false
report " illegal std_logic_vector to string"
severity note;
end case;
end loop;
return out_string;
end;
 
------------------------------------------------------------------------------------
--
------------------------------------------------------------------------------------
 
function hex_data_dblwrdz(in_vector : std_logic_vector) return string is
variable out_string : string(1 to 16);
variable temp_in_vector : std_logic_vector(63 downto 0);
variable vector : std_logic_vector(3 downto 0);
begin
temp_in_vector := in_vector;
for i in 1 to 16 loop
vector := temp_in_vector((67-(4*i)) downto (64-(4*i)));
case vector is
when "0000" => out_string(i) := '0';
when "0001" => out_string(i) := '1';
when "0010" => out_string(i) := '2';
when "0011" => out_string(i) := '3';
when "0100" => out_string(i) := '4';
when "0101" => out_string(i) := '5';
when "0110" => out_string(i) := '6';
when "0111" => out_string(i) := '7';
when "1000" => out_string(i) := '8';
when "1001" => out_string(i) := '9';
when "1010" => out_string(i) := 'A';
when "1011" => out_string(i) := 'B';
when "1100" => out_string(i) := 'C';
when "1101" => out_string(i) := 'D';
when "1110" => out_string(i) := 'E';
when "1111" => out_string(i) := 'F';
when "ZZZZ" => out_string(i) := 'Z';
when others =>
out_string(i) := 'J';
assert false
report " illegal std_logic_vector to string"
severity note;
end case;
end loop;
return out_string;
end;
 
------------------------------------------------------------------------------------
--
------------------------------------------------------------------------------------
-- returns true if the character is a valid hexadecimal character.
function is_hex(c : character) return boolean is
begin
case c is
when '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' |
'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'a' | 'b' | 'c' | 'd' |
'e' | 'f' =>
return(true);
when others =>
return(false);
end case;
end is_hex;
------------------------------------------------------------------------------------
--
------------------------------------------------------------------------------------
-- returns true if the character is a valid hexadecimal character.
function is_std_logic(c : character) return boolean is
begin
case c is
when '0' | '1' | 'u' | 'U' | 'x' | 'X' | 'z' | 'Z' =>
return(true);
when others =>
return(false);
end case;
end is_std_logic;
------------------------------------------------------------------------------------
--
------------------------------------------------------------------------------------
-- returns true if the character is whitespace.
function is_space(c : character) return boolean is
begin
case c is
when ' ' | HT =>
return(true);
when others =>
return(false);
end case;
end is_space;
------------------------------------------------------------------------------------
--
------------------------------------------------------------------------------------
 
function char2sl(in_char : character) return std_logic is
variable out_bit : std_logic;
begin
if (in_char = '1') then
out_bit := '1';
elsif (in_char = '0') then
out_bit := '0';
elsif (in_char = 'x') then
out_bit := 'X';
elsif (in_char = 'X') then
out_bit := 'X';
elsif (in_char = 'u') then
out_bit := 'U';
elsif (in_char = 'U') then
out_bit := 'U';
elsif (in_char = 'z') then
out_bit := 'Z';
elsif (in_char = 'Z') then
out_bit := 'Z';
else
assert false
report "illegal character to std_logic"
severity note;
end if;
 
-- Mysteriously, the following code did not work in place of the
-- above chain of if-elsif-else logic... it seemed to always return '1'.
-- I cannot tell why. -- John Clayton
-- case in_char is
-- when '1' =>
-- out_bit:= '1';
-- when '0' =>
-- out_bit:= '1';
-- when 'u' | 'U' =>
-- out_bit:= 'U';
-- when 'x' | 'X' =>
-- out_bit:= 'X';
-- when 'z' | 'Z' =>
-- out_bit:= 'Z';
-- when others =>
-- assert false
-- report "illegal character to std_logic"
-- severity note;
-- end case;
return out_bit;
end char2sl;
 
 
 
------------------------------------------------------------------------------------
-- Converts Standard Logic Vectors to Unsigned
------------------------------------------------------------------------------------
 
function slv2u(in_a : std_logic_vector) return unsigned is
variable i : natural;
variable o : unsigned(in_a'length-1 downto 0);
 
begin
o := (others=>'0');
for i in 0 to in_a'length-1 loop
o(i) := in_a(i);
end loop;
return(o);
end;
 
------------------------------------------------------------------------------------
-- Converts Unsigned to Standard Logic Vector
------------------------------------------------------------------------------------
 
function u2slv(in_a : unsigned) return std_logic_vector is
variable i : natural;
variable o : std_logic_vector(in_a'length-1 downto 0);
 
begin
o := (others=>'0');
for i in 0 to in_a'length-1 loop
o(i) := in_a(i);
end loop;
return(o);
end;
 
------------------------------------------------------------------------------------
-- Converts Standard Logic Vectors to Signed
------------------------------------------------------------------------------------
 
function slv2s(in_a : std_logic_vector) return signed is
variable i : natural;
variable o : signed(in_a'length-1 downto 0);
 
begin
o := (others=>'0');
for i in 0 to in_a'length-1 loop
o(i) := in_a(i);
end loop;
return(o);
end;
 
------------------------------------------------------------------------------------
-- Converts Signed to Standard Logic Vector
------------------------------------------------------------------------------------
 
function s2slv(in_a : signed) return std_logic_vector is
variable i : natural;
variable o : std_logic_vector(in_a'length-1 downto 0);
 
begin
o := (others=>'0');
for i in 0 to in_a'length-1 loop
o(i) := in_a(i);
end loop;
return(o);
end;
 
------------------------------------------------------------------------------------
-- Resizes Standard Logic Vectors, "right justified" i.e. starts at LSB...
------------------------------------------------------------------------------------
 
function slv_resize(in_vect : std_logic_vector; out_size : integer) return std_logic_vector is
variable i : integer;
variable o_vect : std_logic_vector(out_size-1 downto 0);
 
begin
o_vect := (others=>'0');
for i in 0 to in_vect'length-1 loop
if (i<out_size) then
o_vect(i) := in_vect(i);
end if;
end loop;
return(o_vect);
end slv_resize;
 
------------------------------------------------------------------------------------
-- Resizes Standard Logic Vectors, "left justified" i.e. starts at MSB...
------------------------------------------------------------------------------------
 
function slv_resize_l(in_vect : std_logic_vector; out_size : integer) return std_logic_vector is
variable i : integer;
variable j : integer;
variable o_vect : std_logic_vector(out_size-1 downto 0);
 
begin
o_vect := (others=>'0');
j := out_size-1;
for i in in_vect'length-1 downto 0 loop
if (j>=0) then
o_vect(j) := in_vect(i);
j := j-1;
end if;
end loop;
return(o_vect);
end slv_resize_l;
 
------------------------------------------------------------------------------------
-- Resizes Standard Logic Vectors, "right justified with sign extension"
------------------------------------------------------------------------------------
 
function slv_resize_se(in_vect : std_logic_vector; out_size : integer) return std_logic_vector is
variable i : integer;
variable o_vect : std_logic_vector(out_size-1 downto 0);
 
begin
o_vect := (others=>in_vect(in_vect'length-1));
for i in 0 to in_vect'length-1 loop
if (i<out_size) then
o_vect(i) := in_vect(i);
end if;
end loop;
return(o_vect);
end slv_resize_se;
 
------------------------------------------------------------------------------------
-- Resizes Signed, "right justified" i.e. starts at LSB...
------------------------------------------------------------------------------------
 
function s_resize(in_vect : signed; out_size : integer) return signed is
variable i : integer;
variable o_vect : signed(out_size-1 downto 0);
 
begin
o_vect := (others=>'0');
for i in 0 to in_vect'length-1 loop
if (i<out_size) then
o_vect(i) := in_vect(i);
end if;
end loop;
return(o_vect);
end s_resize;
 
------------------------------------------------------------------------------------
-- Resizes Signed, "left justified" i.e. starts at MSB...
------------------------------------------------------------------------------------
 
function s_resize_l(in_vect : signed; out_size : integer) return signed is
variable i : integer;
variable j : integer;
variable o_vect : signed(out_size-1 downto 0);
 
begin
o_vect := (others=>'0');
j := out_size-1;
for i in in_vect'length-1 downto 0 loop
if (j>=0) then
o_vect(j) := in_vect(i);
j := j-1;
end if;
end loop;
return(o_vect);
end s_resize_l;
 
------------------------------------------------------------------------------------
-- Resizes Signed, "right justified with sign extension"
------------------------------------------------------------------------------------
 
function s_resize_se(in_vect : signed; out_size : integer) return signed is
variable i : integer;
variable o_vect : signed(out_size-1 downto 0);
 
begin
o_vect := (others=>in_vect(in_vect'length-1));
for i in 0 to in_vect'length-1 loop
if (i<out_size) then
o_vect(i) := in_vect(i);
end if;
end loop;
return(o_vect);
end s_resize_se;
 
------------------------------------------------------------------------------------
-- Resizes Unsigned, "right justified" i.e. starts at LSB...
------------------------------------------------------------------------------------
 
function u_resize(in_vect : unsigned; out_size : integer) return unsigned is
variable i : integer;
variable i_vect : unsigned(in_vect'length-1 downto 0);
variable o_vect : unsigned(out_size-1 downto 0);
 
begin
i_vect := in_vect;
o_vect := (others=>'0');
for i in 0 to in_vect'length-1 loop
if (i<out_size) then
o_vect(i) := i_vect(i);
end if;
end loop;
return(o_vect);
end u_resize;
 
------------------------------------------------------------------------------------
-- Resizes Unsigned, "left justified" i.e. starts at MSB...
------------------------------------------------------------------------------------
 
function u_resize_l(in_vect : unsigned; out_size : integer) return unsigned is
variable i : integer;
variable j : integer;
variable o_vect : unsigned(out_size-1 downto 0);
 
begin
o_vect := (others=>'0');
j := out_size-1;
for i in in_vect'length-1 downto 0 loop
if (j>=0) then
o_vect(j) := in_vect(i);
j := j-1;
end if;
end loop;
return(o_vect);
end u_resize_l;
 
------------------------------------------------------------------------------------
-- Selects a slice of the input vector, "right justified" i.e. slice 0 starts at LSB...
------------------------------------------------------------------------------------
 
function u_select(in_vect : unsigned; slice_num : integer; slice_size : integer) return unsigned is
variable i : integer;
variable o_vect : unsigned(slice_size-1 downto 0);
 
begin
o_vect := (others=>'0');
for i in 0 to o_vect'length-1 loop
o_vect(i) := in_vect(i+slice_num*slice_size);
end loop;
return(o_vect);
end u_select;
 
------------------------------------------------------------------------------------
-- Bit Reverses the input vector
------------------------------------------------------------------------------------
 
function u_reverse(in_vect : unsigned) return unsigned is
variable i : integer;
variable o_vect : unsigned(in_vect'length-1 downto 0);
 
begin
for i in in_vect'length-1 downto 0 loop
o_vect(in_vect'length-1-i) := in_vect(i);
end loop;
return(o_vect);
end u_reverse;
 
------------------------------------------------------------------------------------
--
------------------------------------------------------------------------------------
 
function str2u(in_string : string; out_size:integer) return unsigned is
 
variable uval : unsigned(out_size-1 downto 0);
variable nibble : unsigned(3 downto 0);
begin
uval := (others=>'0');
for j in in_string'range loop
uval(uval'length-1 downto 4) := uval(uval'length-5 downto 0);
uval(3 downto 0) := char2u(in_string(j));
end loop;
return uval;
end str2u;
 
------------------------------------------------------------------------------------
--
------------------------------------------------------------------------------------
 
function str2s(in_string : string; out_size:integer) return signed is
 
variable uval : signed(out_size-1 downto 0);
variable nibble : signed(3 downto 0);
begin
uval := (others=>'0');
for j in in_string'range loop
uval(uval'length-1 downto 4) := uval(uval'length-5 downto 0);
uval(3 downto 0) := signed(char2u(in_string(j)));
end loop;
return uval;
end str2s;
 
------------------------------------------------------------------------------------
-- Power Function for naturals
------------------------------------------------------------------------------------
 
-- function "**"(in_a : natural; in_b : natural) return natural is
-- variable i : natural;
-- variable o : natural;
--
-- begin
--
-- -- Coded with a for loop: works in simulation, but Synplify will not synthesize.
---- if (in_b=0) then
---- o := 1;
---- else
---- o := in_a;
---- if (in_b>1) then
---- for i in 2 to in_b loop
---- o := o * in_a;
---- end loop;
---- end if;
---- end if;
---- return(o);
--
-- if (in_b=0) then
-- o := 1;
-- else
-- o := in_a;
-- i := 1;
-- while (i<in_b) loop
-- o := o * in_a;
-- i := i+1;
-- end loop;
-- end if;
-- return(o);
--
-- end;
 
------------------------------------------------------------------------------------
-- Function for 2^(natural)
------------------------------------------------------------------------------------
 
function pow_2_u(in_a : natural; out_size:integer) return unsigned is
variable i : natural;
variable j : natural;
variable o : unsigned(out_size-1 downto 0);
 
begin
 
j := in_a;
o := to_unsigned(1,o'length);
for i in 0 to out_size-1 loop
if (j>0) then
o := o(out_size-2 downto 0) & '0';
j := j-1;
end if;
end loop;
return(o);
end;
 
------------------------------------------------------------------------------------
-- A sort of "barrel shifter." Produces the ASR by in_a of the input...
------------------------------------------------------------------------------------
 
function asr_function(in_vect : signed; in_a : natural) return signed is
variable i : natural;
variable j : natural;
variable o_vect : signed(in_vect'length-1 downto 0);
 
begin
o_vect := in_vect;
j := in_a;
for i in 0 to in_vect'length-1 loop -- Now loop to fill in the actual results
if (j>0) then
o_vect := o_vect(o_vect'length-1) & o_vect(o_vect'length-1 downto 1);
j := j-1;
end if;
end loop;
return(o_vect);
end asr_function;
 
------------------------------------------------------------------------------------
--
------------------------------------------------------------------------------------
 
function bit_width (maxval : integer) return integer is
 
variable w : integer;
begin
if (maxval<2) then
w := 1;
else
w := integer(ceil(log2(real(maxval))));
end if;
 
return w;
end bit_width;
 
------------------------------------------------------------------------------------
--
------------------------------------------------------------------------------------
 
function bit_width (maxval : real) return integer is
 
variable w : integer;
begin
if (maxval<2.0) then
w := 1;
else
w := integer(ceil(log2(maxval)));
end if;
 
return w;
end bit_width;
 
------------------------------------------------------------------------------------
-- Timer width differs from bit width in the following way:
-- Bit width gives a vector large enough to have maxval different states, but
-- timer width gives a vector large enough to hold the quantity maxval.
--
-- The difference is critical when using timers, since they often count down from
-- the initial value, and trigger timeout at a value of 1... So for maxval equal
-- to a power of two, an extra bit must be reserved. This is done by adding one
-- to the maxval input...
------------------------------------------------------------------------------------
 
function timer_width (maxval : integer) return integer is
 
variable w : integer;
begin
if (maxval<2) then
w := 1;
else
w := integer(ceil(log2(real(maxval+1))));
end if;
 
return w;
end timer_width;
 
------------------------------------------------------------------------------------
--
------------------------------------------------------------------------------------
 
function timer_width (maxval : real) return integer is
 
variable w : integer;
begin
if (maxval<2.0) then
w := 1;
else
w := integer(ceil(log2(maxval+1.0)));
end if;
 
return w;
end timer_width;
 
------------------------------------------------------------------------------------
--
------------------------------------------------------------------------------------
 
function num_words (num_bits : integer; bits_per_word : integer) return integer is
 
variable w : integer;
begin
if (num_bits mod bits_per_word /= 0) then
w := integer(ceil(real(num_bits) / real(bits_per_word)));
else
w := integer(floor(real(num_bits) / real(bits_per_word)));
end if;
return w;
end num_words;
 
------------------------------------------------------------------------------------
--
------------------------------------------------------------------------------------
 
function asciichar2u2(d:character) return unsigned is
variable dout : unsigned(0 to 7);
variable ascii_int : integer := 0;
variable val : integer := 0;
begin
-- Get integer value of the character
ascii_int := character'pos(d);
for index in dout'range loop
val := ascii_int rem 2;
ascii_int := ascii_int/2;
if val=0 then
dout(dout'high-index):='0';
else
dout(dout'high-index):='1';
end if;
end loop;
 
return dout;
end asciichar2u2;
 
------------------------------------------------------------------------------------
--
------------------------------------------------------------------------------------
 
-- converts a string into std_logic_vector
 
function str2u(s: string) return unsigned is
variable uv: unsigned(8*s'high-1 downto 0);
variable k: integer;
begin
k := s'high-s'low;
for i in s'range loop
-- uv(8*k+7 downto 8*k) := unsigned(chartobyte(s(i)));
uv(8*k+7 downto 8*k) := asciichar2u2(s(i));
k := k - 1;
end loop;
return uv;
end str2u;
------------------------------------------------------------------------------------
-- Counts the number of ones in an unsigned input
------------------------------------------------------------------------------------
 
function u_ones(in_a : unsigned) return natural is
variable i,c : natural;
 
begin
c := 0;
for i in 0 to in_a'length-1 loop
if (in_a(i)='1') then
c := c+1;
end if;
end loop;
return(c);
end;
 
------------------------------------------------------------------------------------
--
------------------------------------------------------------------------------------
 
end convert_pack;
 
 
 
/irig_regenerator/trunk/rtl/dds_pack.vhd
0,0 → 1,970
--------------------------------------------------------------------------
-- Package of Direct Digital Synthesizer (DDS) components
--
-- NOTE: These components are for producing digital pulse outputs at
-- desired frequencies and/or duty cycles. In other words, there
-- are no modules here which include sinewave lookup tables. For
-- that type of module, please refer to "dds_sine_pack.vhd"
--
 
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
 
package dds_pack is
 
component dds_constant_squarewave
generic (
OUTPUT_FREQ : real; -- Desired output frequency
SYS_CLK_RATE : real; -- underlying clock rate
ACC_BITS : integer -- Bit width of DDS phase accumulator
);
port (
 
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Output
pulse_o : out std_logic;
squarewave_o : out std_logic
);
end component;
 
component dds_squarewave
generic (
ACC_BITS : integer -- Bit width of DDS phase accumulator
);
port (
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Frequency setting
freq_i : in unsigned(ACC_BITS-1 downto 0);
 
-- Output
pulse_o : out std_logic;
squarewave_o : out std_logic
);
end component;
 
component dds_squarewave_phase_load
generic (
ACC_BITS : integer -- Bit width of DDS phase accumulator
);
port (
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Frequency setting
freq_i : in unsigned(ACC_BITS-1 downto 0);
 
-- Synchronous load
phase_i : in unsigned(ACC_BITS-1 downto 0);
phase_ld_i : in std_logic;
 
-- Output
pulse_o : out std_logic;
squarewave_o : out std_logic
);
end component;
 
component dds_constant_clk_en_gen
generic (
OUTPUT_FREQ : real; -- Desired output frequency
SYS_CLK_RATE : real; -- underlying clock rate
ACC_BITS : integer -- Bit width of DDS phase accumulator
);
port (
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Output
clk_en_o : out std_logic
);
end component;
 
component dds_clk_en_gen
generic (
ACC_BITS : integer -- Bit width of DDS phase accumulator
);
port (
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Frequency setting
freq_i : in unsigned(ACC_BITS-1 downto 0);
 
-- Output
clk_en_o : out std_logic
);
end component;
 
component calibrated_clk_en_gen
generic (
SYS_CLK_RATE : real -- underlying clock rate
);
port (
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Frequency setting
freq_i : in unsigned(31 downto 0); -- some MSBs may be ignored
 
-- Output
clk_en_o : out std_logic
);
end component;
 
component dds_pwm_dac
generic (
ACC_BITS : integer -- Bit width of DDS phase accumulator
);
port (
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Frequency setting
freq_i : in unsigned(ACC_BITS-1 downto 0);
 
-- Output
clk_en_o : out std_logic
);
end component;
 
component dds_pwm_dac_srv
generic (
ACC_BITS : integer -- Bit width of DDS phase accumulator
);
port (
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Frequency/Phase settings
phase_load_i : in std_logic;
phase_val_i : in unsigned(ACC_BITS-1 downto 0);
freq_i : in unsigned(ACC_BITS-1 downto 0);
 
-- Output
clk_en_o : out std_logic
);
end component;
 
end dds_pack;
 
package body dds_pack is
end dds_pack;
 
-------------------------------------------------------------------------------
-- Direct Digital Synthesizer Constant Squarewave module
-------------------------------------------------------------------------------
--
-- Author: John Clayton
-- Update: Sep. 5, 2002 copied this file from "auto_baud_pack.vhd"
-- Added tracking functions, and debugged them.
--
-- Description
-------------------------------------------------------------------------------
-- This is a simple direct digital synthesizer module. It includes a phase
-- accumulator which increments in order to produce the desired output
-- frequency in its most significant bit, which is the squarewave output.
--
-- In addition to the squarewave output there is a pulse output which is
-- high for one sys_clk period, during the sys_clk period immediately
-- preceding the rising edge of the squarewave output.
--
-- NOTES:
-- The accumulator increment word is:
-- increment = Fout*2^N/Fsys_clk
--
-- Where N is the number of bits in the phase accumulator.
--
-- There will always be jitter with this type of clock source, but the
-- long time average frequency can be made arbitrarily close to whatever
-- value is desired, simply by increasing N.
--
-- To reduce jitter, use a higher underlying system clock frequency, and
-- for goodness sakes, try to keep the desired output frequency much lower
-- than the system clock frequency. The closer it gets to Fsys_clk/2, the
-- closer it is to the Nyquist limit, and the output jitter is much more
-- significant at that point.
--
--
 
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
 
entity dds_constant_squarewave is
generic (
OUTPUT_FREQ : real := 8000.0; -- Desired output frequency
SYS_CLK_RATE : real := 48000000.0; -- underlying clock rate
ACC_BITS : integer := 16 -- Bit width of DDS phase accumulator
);
port (
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Output
pulse_o : out std_logic;
squarewave_o : out std_logic
);
end dds_constant_squarewave;
 
architecture beh of dds_constant_squarewave is
 
-- Constants
constant DDS_INCREMENT : integer := integer(OUTPUT_FREQ*(2**real(ACC_BITS))/SYS_CLK_RATE);
 
-- Signals
signal dds_phase : unsigned(ACC_BITS-1 downto 0); -- phase accumulator register
signal dds_phase_next : unsigned(ACC_BITS-1 downto 0);
 
-----------------------------------------------------------------------------
begin
 
dds_proc: Process(sys_rst_n,sys_clk)
begin
if (sys_rst_n = '0') then
dds_phase <= (others=>'0');
elsif (sys_clk'event and sys_clk='1') then
if (sys_clk_en='1') then
dds_phase <= dds_phase_next;
end if;
end if; -- sys_clk
end process dds_proc;
dds_phase_next <= dds_phase + DDS_INCREMENT;
pulse_o <= '1' when sys_clk_en='1' and dds_phase(dds_phase'length-1)='0' and dds_phase_next(dds_phase_next'length-1)='1' else '0';
squarewave_o <= dds_phase(dds_phase'length-1);
 
end beh;
 
 
-------------------------------------------------------------------------------
-- Direct Digital Synthesizer Variable Frequency Squarewave module
-------------------------------------------------------------------------------
--
-- Author: John Clayton
-- Update: Jan. 31, 2013 copied code from dds_constant_squarewave, and
-- modified it to accept a frequency setting input.
--
-- Description
-------------------------------------------------------------------------------
-- This is a simple direct digital synthesizer module. It includes a phase
-- accumulator which increments in order to produce the desired output
-- frequency in its most significant bit, which is the squarewave output.
--
-- In addition to the squarewave output there is a pulse output which is
-- high for one sys_clk period, during the sys_clk period immediately
-- preceding the rising edge of the squarewave output.
--
-- NOTES:
-- The accumulator increment word is:
-- increment = Fout*2^N/Fsys_clk
--
-- Where N is the number of bits in the phase accumulator.
--
-- There will always be jitter with this type of clock source, but the
-- long time average frequency can be made arbitrarily close to whatever
-- value is desired, simply by increasing N.
--
-- To reduce jitter, use a higher underlying system clock frequency, and
-- for goodness sakes, try to keep the desired output frequency much lower
-- than the system clock frequency. The closer it gets to Fsys_clk/2, the
-- closer it is to the Nyquist limit, and the output jitter is much more
-- significant as compared to the output period at that point.
--
--
 
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
 
entity dds_squarewave is
generic (
ACC_BITS : integer := 16 -- Bit width of DDS phase accumulator
);
port (
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Frequency setting
freq_i : in unsigned(ACC_BITS-1 downto 0);
 
-- Output
pulse_o : out std_logic;
squarewave_o : out std_logic
);
end dds_squarewave;
 
architecture beh of dds_squarewave is
 
-- Signals
signal dds_phase : unsigned(ACC_BITS-1 downto 0); -- phase accumulator register
signal dds_phase_next : unsigned(ACC_BITS-1 downto 0);
 
-----------------------------------------------------------------------------
begin
 
dds_proc: Process(sys_rst_n,sys_clk)
begin
if (sys_rst_n = '0') then
dds_phase <= (others=>'0');
elsif (sys_clk'event and sys_clk='1') then
if (sys_clk_en='1') then
dds_phase <= dds_phase_next;
end if;
end if; -- sys_clk
end process dds_proc;
dds_phase_next <= dds_phase + freq_i;
pulse_o <= '1' when sys_clk_en='1' and dds_phase(dds_phase'length-1)='0' and dds_phase_next(dds_phase_next'length-1)='1' else '0';
squarewave_o <= dds_phase(dds_phase'length-1);
 
end beh;
 
 
-------------------------------------------------------------------------------
-- Direct Digital Synthesizer Variable Frequency Squarewave module,
-- with synchronous phase load
-------------------------------------------------------------------------------
--
-- Author: John Clayton
-- Update: Aug. 1, 2013 copied code from dds_squarewave, and
-- modified it to accept a synchronous load input.
--
-- Description
-------------------------------------------------------------------------------
-- This is a simple direct digital synthesizer module. It includes a phase
-- accumulator which increments in order to produce the desired output
-- frequency in its most significant bit, which is the squarewave output.
--
-- In addition to the squarewave output there is a pulse output which is
-- high for one sys_clk period, during the sys_clk period immediately
-- preceding the rising edge of the squarewave output.
--
-- A synchronous load input allows the synthesizer to be adjusted to any
-- desired initial phase condition. This is useful when using it for
-- timing and synchronization.
--
-- NOTES:
-- The accumulator increment word is:
-- increment = Fout*2^N/Fsys_clk
--
-- Where N is the number of bits in the phase accumulator.
--
-- There will always be jitter with this type of clock source, but the
-- long time average frequency can be made arbitrarily close to whatever
-- value is desired, simply by increasing N.
--
-- To reduce jitter, use a higher underlying system clock frequency, and
-- for goodness sakes, try to keep the desired output frequency much lower
-- than the system clock frequency. The closer it gets to Fsys_clk/2, the
-- closer it is to the Nyquist limit, and the output jitter is much more
-- significant as compared to the output period at that point.
--
--
 
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
 
entity dds_squarewave_phase_load is
generic (
ACC_BITS : integer := 16 -- Bit width of DDS phase accumulator
);
port (
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Frequency setting
freq_i : in unsigned(ACC_BITS-1 downto 0);
 
-- Synchronous load
phase_i : in unsigned(ACC_BITS-1 downto 0);
phase_ld_i : in std_logic;
 
-- Output
pulse_o : out std_logic;
squarewave_o : out std_logic
);
end dds_squarewave_phase_load;
 
architecture beh of dds_squarewave_phase_load is
 
-- Signals
signal dds_phase : unsigned(ACC_BITS-1 downto 0); -- phase accumulator register
signal dds_phase_next : unsigned(ACC_BITS-1 downto 0);
 
-----------------------------------------------------------------------------
begin
 
dds_proc: Process(sys_rst_n,sys_clk)
begin
if (sys_rst_n = '0') then
dds_phase <= (others=>'0');
elsif (sys_clk'event and sys_clk='1') then
if (sys_clk_en='1') then
dds_phase <= dds_phase_next;
end if;
if (phase_ld_i='1') then
dds_phase <= phase_i;
end if;
end if; -- sys_clk
end process dds_proc;
dds_phase_next <= dds_phase + freq_i;
pulse_o <= '1' when sys_clk_en='1' and dds_phase(dds_phase'length-1)='0' and dds_phase_next(dds_phase_next'length-1)='1' else '0';
squarewave_o <= dds_phase(dds_phase'length-1);
 
end beh;
 
 
-------------------------------------------------------------------------------
-- Direct Digital Synthesizer Clock Enable Generator, Constant output frequency
-------------------------------------------------------------------------------
--
-- Author: John Clayton
-- Update: Oct. 4, 2013 Copied code from dds_clk_en_gen, and modified it to
-- create a clock enable output using generics to set
-- the frequency.
--
-- Description
-------------------------------------------------------------------------------
-- This is a simple direct digital synthesizer module. It includes a phase
-- accumulator which increments in order to produce the desired output
-- frequency in its most significant bit, which is a squarewave with
-- some unavoidable jitter.
--
-- In this module, the squarewave is not provided as an output, because
-- this module's purpose is to generate a clock enable signal.
--
-- The clock enable output is a pulsed output which, for frequencies below
-- the Nyquist frequency is high for one sys_clk period, during the sys_clk
-- period immediately preceding the rising edge of the squarewave output.
--
-- A special "trick" is performed inside this module, which is to invert the
-- pulse output for frequencies above the Nyquist frequency. Since the pulse
-- train is a perfect 50% duty cycle squarewave at the Nyquist frequency, the
-- pulse train and its inverse are equivalent at that point... But for
-- higher frequencies, the squarewave reduces in frequency back down towards
-- zero Hz. However, since the pulse train is inverted for frequencies above
-- the Nyquist frequency (Fsys_clk/2), then the output pulse train is high for
-- a larger and larger fraction of time... essentially forming a nice clock
-- enable which can be varied from 0% duty cycle, all the way up to N% duty
-- cycle, where K is given by:
--
-- K = max_duty_cycle = 100 * (1-2**(-N)) percent
--
-- Where N is the number of bits in the phase accumulator (ACC_BITS)
--
--
-- NOTES:
-- The accumulator increment word is set by generics:
-- increment = OUTPUT_FREQ*2^ACC_BITS/SYS_CLK_RATE
--
-- Where N is the number of bits in the phase accumulator (ACC_BITS)
--
-- There will always be jitter with this type of clock source, but the
-- clock enable output duty cycle can be adjusted in increments as fine
-- as may be desired, simply by increasing N.
--
--
 
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
 
entity dds_constant_clk_en_gen is
generic (
OUTPUT_FREQ : real := 32000000.0; -- Desired output frequency
SYS_CLK_RATE : real := 50000000.0; -- underlying clock rate
ACC_BITS : integer := 30 -- Bit width of DDS phase accumulator
);
port (
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Output
clk_en_o : out std_logic
);
end dds_constant_clk_en_gen;
 
architecture beh of dds_constant_clk_en_gen is
 
-- Constants
constant DDS_INCREMENT : integer := integer(OUTPUT_FREQ*(2**real(ACC_BITS))/SYS_CLK_RATE);
constant HALFWAY : integer := integer(2**real(ACC_BITS-1));
 
-- Signals
signal dds_phase : unsigned(ACC_BITS-1 downto 0); -- phase accumulator register
signal dds_phase_next : unsigned(ACC_BITS-1 downto 0);
signal pulse_l : std_logic;
 
-----------------------------------------------------------------------------
begin
 
dds_proc: Process(sys_rst_n,sys_clk)
begin
if (sys_rst_n = '0') then
dds_phase <= (others=>'0');
elsif (sys_clk'event and sys_clk='1') then
if (sys_clk_en='1') then
dds_phase <= dds_phase_next;
end if;
end if; -- sys_clk
end process dds_proc;
dds_phase_next <= dds_phase + DDS_INCREMENT;
pulse_l <= '1' when sys_clk_en='1' and dds_phase(dds_phase'length-1)='0' and dds_phase_next(dds_phase_next'length-1)='1' else '0';
clk_en_o <= pulse_l when (DDS_INCREMENT<HALFWAY) else not pulse_l;
 
end beh;
 
 
-------------------------------------------------------------------------------
-- Direct Digital Synthesizer Clock Enable Generator
-------------------------------------------------------------------------------
--
-- Author: John Clayton
-- Update: Oct. 4, 2013 Copied code from dds_squarewave, and modified it to
-- create a clock enable output which essentially has
-- a duty cycle that varies from zero up to 100%. This
-- is similar in some ways to a first order sigma-delta
-- modulator, my friend! It can be used as a DAC, if
-- the sys_clk_en is high. See dds_pwm_dac for a
-- similar unit that allows slowing down the output
-- waveform according to the period between sys_clk_en
-- pulses.
--
-- Description
-------------------------------------------------------------------------------
-- This is a simple direct digital synthesizer module. It includes a phase
-- accumulator which increments in order to produce the desired output
-- frequency in its most significant bit, which is a squarewave with
-- some unavoidable jitter.
--
-- In this module, the squarewave is not provided as an output, because
-- this module's purpose is to generate a clock enable signal.
--
-- The clock enable output is a pulsed output which, for frequencies below
-- the Nyquist frequency is high for one sys_clk period, during the sys_clk
-- period immediately preceding the rising edge of the squarewave output.
--
-- A special "trick" is performed inside this module, which is to invert the
-- pulse output for frequencies above the Nyquist frequency. Since the pulse
-- train is a perfect 50% duty cycle squarewave at the Nyquist frequency, the
-- pulse train and its inverse are equivalent at that point... But for
-- higher frequencies, the squarewave reduces in frequency back down towards
-- zero Hz. However, since the pulse train is inverted for frequencies above
-- the Nyquist frequency (Fsys_clk/2), then the output pulse train is high for
-- a larger and larger fraction of time... essentially forming a nice clock
-- enable which can be varied from 0% duty cycle, all the way up to N% duty
-- cycle, where K is given by:
--
-- K = max_duty_cycle = 100 * (1-2**(-N)) percent
--
-- Where N is the number of bits in the phase accumulator (ACC_BITS)
--
-- This can perhaps operate as a form of PWM also... although the fundamental
-- frequency is not constant, as it is in true PWM.
--
--
-- NOTES:
-- The accumulator increment word is:
-- increment = Fout*2^N/Fsys_clk
--
-- Where N is the number of bits in the phase accumulator (ACC_BITS)
--
-- There will always be jitter with this type of clock source, but the
-- clock enable output duty cycle can be adjusted in increments as fine
-- as may be desired, simply by increasing N.
--
--
 
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
 
entity dds_clk_en_gen is
generic (
ACC_BITS : integer := 16 -- Bit width of DDS phase accumulator
);
port (
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Frequency setting
freq_i : in unsigned(ACC_BITS-1 downto 0);
 
-- Output
clk_en_o : out std_logic
);
end dds_clk_en_gen;
 
architecture beh of dds_clk_en_gen is
 
-- Signals
signal dds_phase : unsigned(ACC_BITS-1 downto 0); -- phase accumulator register
signal dds_phase_next : unsigned(ACC_BITS-1 downto 0);
signal pulse_l : std_logic;
 
-----------------------------------------------------------------------------
begin
 
dds_proc: Process(sys_rst_n,sys_clk)
begin
if (sys_rst_n = '0') then
dds_phase <= (others=>'0');
elsif (sys_clk'event and sys_clk='1') then
if (sys_clk_en='1') then
dds_phase <= dds_phase_next;
end if;
end if; -- sys_clk
end process dds_proc;
dds_phase_next <= dds_phase + freq_i;
pulse_l <= '1' when sys_clk_en='1' and dds_phase(dds_phase'length-1)='0' and dds_phase_next(dds_phase_next'length-1)='1' else '0';
clk_en_o <= pulse_l when freq_i(freq_i'length-1)='0' else not pulse_l;
 
end beh;
 
 
-------------------------------------------------------------------------------
-- Calibrated Direct Digital Synthesizer Clock Enable Generator
-------------------------------------------------------------------------------
--
-- Author: John Clayton
-- Update: Nov. 11, 2013 Copied code from dds_clk_en_gen, and modified it.
--
-- Description
-------------------------------------------------------------------------------
-- This is two DDS units tied together. The first unit produces a calibration
-- clock enable, at a frequency which is very close to a power of two Hz.
-- It uses the highest power of two which can be achieved using the given
-- system clock frequency.
--
-- The second DDS unit then uses the calibration clock enable for its
-- clock enable input. This effectively "calibrates" the second unit
-- so that its input frequency is in Hz.
--
-- The input frequency to this module was fixed at 32 bits, but the
-- most significant bits may be ignored.
--
 
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
 
library work;
use work.dds_pack.all;
use work.convert_pack.all;
 
entity calibrated_clk_en_gen is
generic (
SYS_CLK_RATE : real := 50000000.0 -- underlying clock rate
);
port (
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Frequency setting
freq_i : in unsigned(31 downto 0);
 
-- Output
clk_en_o : out std_logic
);
end calibrated_clk_en_gen;
 
architecture beh of calibrated_clk_en_gen is
 
-- Constants
constant LOG2_CAL_BITS : natural := bit_width(SYS_CLK_RATE)-1;
 
-- Signals
signal cal_clk_en : std_logic;
 
 
 
-----------------------------------------------------------------------------
begin
 
-- Calibration clock enable
cal_clk_gen : dds_constant_clk_en_gen
generic map(
OUTPUT_FREQ => real(2**LOG2_CAL_BITS),
SYS_CLK_RATE => SYS_CLK_RATE,
ACC_BITS => 30
)
port map(
sys_rst_n => sys_rst_n,
sys_clk => sys_clk,
sys_clk_en => sys_clk_en,
 
-- Output
clk_en_o => cal_clk_en
);
 
clk_en_gen : dds_clk_en_gen
generic map(
ACC_BITS => LOG2_CAL_BITS
)
port map(
sys_rst_n => sys_rst_n,
sys_clk => sys_clk,
sys_clk_en => cal_clk_en,
 
-- Frequency setting
freq_i => freq_i(LOG2_CAL_BITS-1 downto 0),
 
-- Output
clk_en_o => clk_en_o
);
 
end beh;
 
 
-------------------------------------------------------------------------------
-- Direct Digital Synthesizer PWM DAC
-------------------------------------------------------------------------------
--
-- Author: John Clayton
-- Update:
-- Jan. 23, 2015 Copied code from clk_en_gen, and brought pulse_l
-- logic inside the sys_clk_en enabled process, so
-- that the pulses are widened according to sys_clk_en.
--
-- Description
-------------------------------------------------------------------------------
-- This is exactly the same as dds_clk_en_gen, except that it brings the pulse_l
-- logic inside the clocked process, so that output pulses are no longer 1
-- sys_clk wide, but are instead lengthened according to sys_clk_en. This makes
-- the unit useful for generating a PWM output signal, with the smallest pulse
-- being the period between sys_clk_en pulses.
--
-- This is a simple direct digital synthesizer module. It includes a phase
-- accumulator which increments in order to produce the desired output
-- frequency in its most significant bit, which is a squarewave with
-- some unavoidable jitter.
--
-- In this module, the squarewave is not provided as an output, because
-- this module's purpose is to generate a clock enable signal.
--
-- The clock enable output is a pulsed output which, for frequencies below
-- the Nyquist frequency is high for one sys_clk period, during the sys_clk
-- period immediately preceding the rising edge of the squarewave output.
--
-- A special "trick" is performed inside this module, which is to invert the
-- pulse output for frequencies above the Nyquist frequency. Since the pulse
-- train is a perfect 50% duty cycle squarewave at the Nyquist frequency, the
-- pulse train and its inverse are equivalent at that point... But for
-- higher frequencies, the squarewave reduces in frequency back down towards
-- zero Hz. However, since the pulse train is inverted for frequencies above
-- the Nyquist frequency (Fsys_clk/2), then the output pulse train is high for
-- a larger and larger fraction of time... essentially forming a nice clock
-- enable which can be varied from 0% duty cycle, all the way up to N% duty
-- cycle, where K is given by:
--
-- K = max_duty_cycle = 100 * (1-2**(-N)) percent
--
-- Where N is the number of bits in the phase accumulator (ACC_BITS)
--
-- This can perhaps operate as a form of PWM also... although the fundamental
-- frequency is not constant, as it is in true PWM.
--
--
-- NOTES:
-- The accumulator increment word is:
-- increment = Fout*2^N/Fsys_clk
--
-- Where N is the number of bits in the phase accumulator (ACC_BITS)
--
-- There will always be jitter with this type of clock source, but the
-- clock enable output duty cycle can be adjusted in increments as fine
-- as may be desired, simply by increasing N.
--
--
 
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
 
entity dds_pwm_dac is
generic (
ACC_BITS : integer := 16 -- Bit width of DDS phase accumulator
);
port (
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Frequency setting
freq_i : in unsigned(ACC_BITS-1 downto 0);
 
-- Output
clk_en_o : out std_logic
);
end dds_pwm_dac;
 
architecture beh of dds_pwm_dac is
 
-- Signals
signal dds_phase : unsigned(ACC_BITS-1 downto 0); -- phase accumulator register
signal dds_phase_next : unsigned(ACC_BITS-1 downto 0);
signal pulse_l : std_logic;
 
-----------------------------------------------------------------------------
begin
 
dds_proc: Process(sys_rst_n,sys_clk)
begin
if (sys_rst_n = '0') then
dds_phase <= (others=>'0');
pulse_l <= '0';
elsif (sys_clk'event and sys_clk='1') then
if (sys_clk_en='1') then
dds_phase <= dds_phase_next;
if (dds_phase(dds_phase'length-1)='0' and dds_phase_next(dds_phase_next'length-1)='1') then
pulse_l <= '1';
else
pulse_l <= '0';
end if;
end if;
end if; -- sys_clk
end process dds_proc;
dds_phase_next <= dds_phase + freq_i;
clk_en_o <= pulse_l when freq_i(freq_i'length-1)='0' else not pulse_l;
 
end beh;
 
-------------------------------------------------------------------------------
-- Direct Digital Synthesizer PWM DAC - Synchronously Resettable Version
-------------------------------------------------------------------------------
--
-- Author: John Clayton
-- Update:
-- Jan. 23, 2015 Copied code from dds_pwm_dac, and added synchronous
-- phase load input and phase value inputs
--
-- Description
-------------------------------------------------------------------------------
-- This is exactly the same as dds_pwm_dac, except that a synchronous phase
-- load is provided. This was needed when using the DAC to "throttle" pixel
-- counters driving a display. By resetting the DAC's phase accumulator
-- during each retrace interval, visible "jitter" was reduced, and made
-- identical for each line of display pixels.
--
-- NOTES:
-- The accumulator increment word is:
-- increment = Fout*2^N/Fsys_clk
--
-- Where N is the number of bits in the phase accumulator (ACC_BITS)
--
-- There will always be jitter with this type of clock source, but the
-- clock enable output duty cycle can be adjusted in increments as fine
-- as may be desired, simply by increasing N.
--
--
 
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
 
entity dds_pwm_dac_srv is
generic (
ACC_BITS : integer := 16 -- Bit width of DDS phase accumulator
);
port (
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Frequency/Phase settings
phase_load_i : in std_logic;
phase_val_i : in unsigned(ACC_BITS-1 downto 0);
freq_i : in unsigned(ACC_BITS-1 downto 0);
 
-- Output
clk_en_o : out std_logic
);
end dds_pwm_dac_srv;
 
architecture beh of dds_pwm_dac_srv is
 
-- Signals
signal dds_phase : unsigned(ACC_BITS-1 downto 0); -- phase accumulator register
signal dds_phase_next : unsigned(ACC_BITS-1 downto 0);
signal pulse_l : std_logic;
 
-----------------------------------------------------------------------------
begin
 
dds_proc: Process(sys_rst_n,sys_clk)
begin
if (sys_rst_n = '0') then
dds_phase <= (others=>'0');
pulse_l <= '0';
elsif (sys_clk'event and sys_clk='1') then
if (sys_clk_en='1') then
if (phase_load_i='1') then
dds_phase <= phase_val_i;
pulse_l <= '0';
else
dds_phase <= dds_phase_next;
if (dds_phase(dds_phase'length-1)='0' and dds_phase_next(dds_phase_next'length-1)='1') then
pulse_l <= '1';
else
pulse_l <= '0';
end if;
end if; -- phase_zero_i
end if; -- sys_clk_en
end if; -- sys_clk
end process dds_proc;
dds_phase_next <= dds_phase + freq_i;
clk_en_o <= pulse_l when freq_i(freq_i'length-1)='0' else not pulse_l;
 
end beh;
 
 
/irig_regenerator/trunk/rtl/irig_time_pack.vhd
0,0 → 1,2846
--------------------------------------------------------------------------
-- Package of IRIG time pattern generator components
--
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
 
package irig_time_pack is
 
-- This component includes the capability to output both DC Level and modulated timecode streams.
-- It has been coded with 32 bit wide registers
component irig_time_generator
generic (
SYS_CLK_RATE : real; -- Needed for carrier generation
DEF_R_0 : unsigned(31 downto 0); -- Years, Days
DEF_R_1 : unsigned(31 downto 0); -- Hours, Minutes, Seconds
DEF_R_2 : unsigned(31 downto 0); -- Control(31:0)
DEF_R_3 : unsigned(31 downto 0); -- Control(44:32)
DEF_R_4 : unsigned(31 downto 0); -- Rate Setting
DEF_R_5 : unsigned(31 downto 0); -- Fields Used [ctrl,sbs,year]
DEF_R_6 : unsigned(31 downto 0); -- Carrier Frequency
DEF_R_Z : unsigned(31 downto 0) -- Value returned for nonexistent registers
);
port (
 
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Bus interface
adr_i : in unsigned(3 downto 0);
sel_i : in std_logic;
we_i : in std_logic;
dat_i : in unsigned(31 downto 0);
dat_o : out unsigned(31 downto 0);
ack_o : out std_logic;
 
-- Pulse per second time reference
pps_i : in std_logic;
pps_ext_i : in std_logic;
 
-- IRIG select inputs
-- Active only when register 0x4 set to zero.
rate_i : in unsigned(2 downto 0); -- Rates : 0,1=>B, 2=>A, 3=>G, 4=>NASA 36-bit
carrier_i : in unsigned(2 downto 0); -- Carriers : 0=DCLS, 1=100Hz, 2=1kHz, 3=10kHz, 4=100kHz
codes_i : in unsigned(2 downto 0); -- Codes : (2)=> control, (1)=> SBS, (0)=> year
 
-- Serial IRIG time stream, LVTTL level output
time_dcl_o : out std_logic;
cap_gnd_o : out unsigned(2 downto 0);
ref_o : out std_logic;
carrier_o : out unsigned(1 downto 0)
);
end component;
 
-- This component includes the capability to receive DC Level timecode streams.
-- External circuitry should low-pass filter the timecode signal to remove the
-- carrier frequency, and pass the envelope signal directly
-- to this module, where it is treated as a digital input.
-- Although it is a receiver, it also acts as a "synchronized transmitter."
component irig_time_regenerator
generic (
SYS_CLK_RATE : real;
LOCK_THRESHOLD : integer; -- Packets received before entering locked state
DEF_R_0 : unsigned(31 downto 0); -- Years, Days
DEF_R_1 : unsigned(31 downto 0); -- Hours, Minutes, Seconds
DEF_R_2 : unsigned(31 downto 0); -- Control(31:0)
DEF_R_3 : unsigned(31 downto 0); -- Control(44:32)
DEF_R_4 : unsigned(31 downto 0); -- Rate Setting
DEF_R_5 : unsigned(31 downto 0); -- Fields Used [ctrl,sbs,year]
DEF_R_6 : unsigned(31 downto 0); -- Carrier Frequency
DEF_R_A : unsigned(31 downto 0); -- Initial value of carrier detect bias
DEF_R_Z : unsigned(31 downto 0) -- Value returned for nonexistent registers
);
port (
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Bus interface
adr_i : in unsigned(3 downto 0);
sel_i : in std_logic;
we_i : in std_logic;
dat_i : in unsigned(31 downto 0);
dat_o : out unsigned(31 downto 0);
ack_o : out std_logic;
 
-- IRIG select inputs
-- Active only when register 0x4 set to zero.
rate_i : in unsigned(2 downto 0); -- Rates : 0,1=>B, 2=>A, 3=>G, 4=>NASA 36-bit
carrier_i : in unsigned(2 downto 0); -- Carriers : 0=DCLS, 1=100Hz, 2=1kHz, 3=10kHz, 4=100kHz
codes_i : in unsigned(2 downto 0); -- Codes : (2)=> control, (1)=> SBS, (0)=> year
 
-- IRIG time output, parallel form
time_o : out unsigned(67 downto 0);
 
-- Carrier Detect Select output, PPS input,
-- IRIG time input, tracking and lock indicators
cdet_sel_o : out std_logic;
pps_i : in std_logic;
time_cdet_i : in std_logic;
cdet_val_o : out unsigned(1 downto 0);
active_o : out std_logic;
tracking_o : out std_logic;
lock_o : out std_logic;
neverlocked_o : out std_logic;
 
-- Serial IRIG time stream output, LVTTL level
time_dcl_o : out std_logic;
cap_gnd_o : out std_logic;
ref_o : out std_logic;
carrier_o : out unsigned(1 downto 0)
);
end component;
 
end irig_time_pack;
 
package body irig_time_pack is
end irig_time_pack;
 
 
-------------------------------------------------------------------------------
-- IRIG time pattern generator core
-------------------------------------------------------------------------------
--
-- Author: John Clayton
-- Date : Oct. 14, 2010 Copied code from irig_time, changed registers to
-- a 32-bit format.
-- Nov. 24, 2010 Added DEF_R_Z generic parameter
-- Mar. 28, 2012 Moved clk_en_1mhz logic inside this module, updated
-- description. Added "pps_ext_i" input. Changed name
-- to "irig_time_generator"
-- Apr. 4, 2012 Swapped R0 and R1, so that years & days are first.
-- This arrangement will be more congruent with the
-- IRIG time receiver.
-- Apr. 5, 2012 Added support for NASA 36-bit (WWV) format, by
-- extending the rate setting to include a new value,
-- of 4.
-- Apr. 30, 2013 Changed SYS_CLK_RATE from integer to real type.
-- Dec. 2, 2013 Decreased SQF_BITS from 6 to 4, in order to increase
-- the frequency of the square_fast signal by a factor
-- of 4.
-- Mar. 11, 2015 Added leap_year bit to register zero. Prevented
-- day values higher than 366 from persisting.
--
-- Description
-------------------------------------------------------------------------------
-- This time pattern generator outputs DC Level data, plus a pair of digital outputs
-- which contain a modulated carrier signal. The modulated carrier frequency is
-- the low-frequency heterodyne signal generated by digital mixing of a 500 kHz
-- squarewave with a 501 kHz (IRIG "B"), 510 kHz (IRIG "A") or 600 kHz (IRIG "G")
-- squarewave, as generated by a DDS. As such, the digital output needs to be
-- low-pass filtered in order to cut down the amplitude of the high-frequency
-- heterodyne signal, plus the 3rd and higher order harmonics of the squarewaves
-- used. In order to modulate the amplitude of the resulting low-pass filtered
-- carrier over a 3:1 ratio (An AM modulation index of 0.5, meaning 50% modulation
-- depth) a 2-bit R-2R resistor ladder network DAC is recommended for use outside
-- the FPGA. Also, in order to keep the modulated signal balanced, a reference
-- voltage other than ground is used. The reference voltage is nominally 1/2 of
-- the FPGA VCCo supply voltage (3.3V * 0.5 = 1.65V). The FPGA generates this
-- reference voltage through the use of an output which provides a 50% duty cycle
-- squarewave. The schematic is as follows:
--
-- R 1.65V R R
-- ref_o o---/\/\--.--/\/\--/\/\--.
-- | |
-- Cref - + |
-- 15uF - |
-- | |
-- GND |
-- R R |
-- carrier_o(0) o------------/\/\--/\/\--.
-- (LSB) |
-- R <
-- < Cout
-- R R | 4.7uF
-- carrier_o(1) o------------/\/\--/\/\--.-----|(-------o AC coupled, Modulated Output
-- (MSB) |
-- Cfilt |
-- 0.01uF |
-- cap_gnd_o(0) o------------|(----------.
-- |
-- Cfilt |
-- 0.01uF |
-- cap_gnd_o(1) o------------|(----------.
-- |
-- Cfilt |
-- 1300pF |
-- cap_gnd_o(2) o------------|(----------.
--
-- In practice, R=270 ohms has been successfully used.
-- The Cfilt values are not exact, but the values shown worked well enough.
--
-- The two carrier signals are identical, except that the LSB is constantly driving
-- the heterodyne signal out, while the MSB is only driving the heterodyne signal
-- during times when the irig DC level pulse is high. The cap_gnd_o outputs serves
-- to adjust the corner frequency of the RC low pass filter. They are not pulsed
-- with any high frequency signals. They simply apply ground or else high impedance,
-- thereby effectively bringing the Cfilt capacitors in and out of the circuit.
--
-- It is recommended to use the Cout capacitor in series with the output to provide
-- AC coupling of the signal, and some DC isolation for the FPGA.
--
-- This core uses a 1MHz clock enable pulse, to count the passage of time
-- and output a pulse stream which conforms to IRIG standard 200-04, for the
-- following IRIG formats:
--
-- First letter: B (100 pps), A (1000 pps) or G (10000 pps)
-- First digit : 0 = DC Level shift output (no modulation)
-- Second digit : 0 = No carrier
-- Third digit : [0..7] (BCD plus combinations of Year, CF and SBS fields)
--
-- The core can generate its own 1 MHz clock enable by dividing the sys_clk
-- by an integer divisor. If no integer divisor can be found, then then
-- perhaps an NCO should be used to come arbitrarily close to the desired
-- 1 MHz rate. No NCO is currently provided, so if you run into this situation
-- you may have to buckle up, and start coding.
--
-- Keeping the time accurate over the longer term is accomplished by using
-- the pps_i input, a single pulse per second, one sys_clk in duration.
-- The pps_i input may be supplied by an external GPS module for accurate time,
-- although in the absence of GPS reference, the local system clock can be
-- used to create a somewhat less accurate reference. An internal pps
-- generator creates a pps signal by counting one million of the 1 MHz clock
-- enable pulses, which is useful as a surrogate when there is no external pps
-- reference. To use the external pps_i input, simply make "pps_ext_i" high.
--
-- All settings for the core are contained in read/write registers, which
-- have default values determined by generics. In the case of a "code"
-- selection which includes straight binary seconds ("SBS"), the control
-- bits for the P8 and P9 portions of the output are not used, and the
-- internally calculated SBS field is used instead.
--
-- The year field also displaces control bits when it is selected. The two
-- digit year is fully supported, and will increment properly at the end of
-- 23:59:59 on day 364. (No leap year).
--
-- For rates A and G, there is a tenths of a second BCD digit present in the
-- latter end of the P4 period.
--
-- For rate G, there is a hundredths of a second BCD digit present in the
-- former end of the P5 period.
--
-- If the year is desired, it can be placed in the proper position within
-- the control bits, as shown here...
-- Format B or A :
-- Year_1 (the most significant BCD digit) =>
-- control_1(0) & control_0(7 downto 5)
-- control_0(4) remains '0'
-- Year_0 (the least significant BCD digit) =>
-- control_0(3 downto 0)
--
-- Format G :
-- Year_1 (the most significant BCD digit) =>
-- control_2(1 downto 0) & control_1(7 downto 6)
-- control_1(5) remains '0'
-- Year_0 (the least significant BCD digit) =>
-- control_1(4 downto 1)
--
-- Bus interface writes to the registers cannot be given higher priority
-- than internal updates for timekeeping based on pps_i, since bus writes
-- only affect one value, while timekeeping can occasionally alter all of
-- them. Thus corrupt and undesired behavior could result if bus writes
-- were to override the timekeeping update values. Therefore, the ack_o
-- signal is used to ensure that the timekeeping is finished before bus
-- writes take place.
--
-- The registers are summarized as follows:
--
-- Address Structure Function
-- ------- ---------- ----------------------------------------
-- 0x0 [**YY*DDD] BCD Year, Day of Year (Day is 1..365 or 366)
-- 0x1 [**HHMMSS] BCD Hours, Minutes, Seconds. (*=unused nibble)
-- 0x2 [CCCCCCCC] Control Bits (31:0)
-- 0x3 [**CCCCCC] Control Bits (44:32)
-- 0x4 (2:0) Rate Setting (See Notes below)
-- 0x5 (2:0) Codes Used [ctrl,SBS,year]
-- 0x6 (2:0) Carrier Modulation Frequency
--
-- Notes:
--
-- Register 0x0 (Leap Year, Year, Day of Year)
--
-- Bit 28 controls the leap year setting:
-- This register currently has only a single bit for setting leap year
-- behavior. When the leap year bit is set, the day field advances to 366
-- before rolling over back to 1. When the bit is clear, the day field
-- to 1 after the 365 day value. Note that the DDD field can be written
-- with values higher than 366, but it will immediately roll over.
--
-- Register 0x4 (Rate)
-- 000 => (Use external input for Rate/Form/Carrier/Codes)*
-- 001 => IRIG-B (1 second updates)
-- 010 => IRIG-A (0.1 second updates)
-- 011 => IRIG-G (0.01 second updates)
-- 100 => NASA 36-bit (WWV) time code
--
-- The "rate_i" input is used along with the register setting, in such
-- a way that the register Rate value being zero, causes the "rate_i" and
-- "codes_i" inputs to be used. If, however, the register Rate is set to
-- a non-zero value, then the external input is ignored.
--
-- When external rate input is used, a setting of "000" results
-- in IRIG-B (1 second updates.) All other settings remain
-- as outlined above.
--
-- Register 0x5 (Codes)
-- Each bit position, when set, activates the use of a specific field
-- within the output, according to the form:
--
-- [ctrl,SBS,year]
--
-- Where ctrl = Control field
-- SBS = Straight Binary Seconds
-- year = Year field
--
-- If year and control are both selected, then the year is used in its
-- appropriate spot, preventing the control bits there from being used.
--
-- For Rate "G", the hundredths of a second field occludes the first four
-- control bits, so that they are not used. The hundredths of a second field
-- is encoded in the register area for "year" since year is not used in
-- Rate "G"
--
-- Register 0x6 (Carrier)
-- 000 => No carrier (DC Level Shift)
-- 001 => 100 Hz / 10 ms. resolution
-- 010 => 1 kHz / 1 millisecond resolution
-- 011 => 10 kHz / 100 microsecond resolution
-- 100 => 100 kHz / 10 microsecond resolution
--
-- Internal logic takes the day, hour, minute and second values and derives
-- a value for the SBS field (Straight Binary Second).
--
-- The generics used as defaults for the registers are the full width of the
-- data bus. However, only those bits which are actually present in the actual
-- registers are used.
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
 
library work;
use work.convert_pack.all;
 
entity irig_time_generator is
generic (
SYS_CLK_RATE : real := 40000000.0; -- Needed for carrier generation
DEF_R_0 : unsigned(31 downto 0) := str2u("00100123",32);
DEF_R_1 : unsigned(31 downto 0) := str2u("00000000",32);
DEF_R_2 : unsigned(31 downto 0) := str2u("00000000",32);
DEF_R_3 : unsigned(31 downto 0) := str2u("00000000",32);
DEF_R_4 : unsigned(31 downto 0) := str2u("00000001",32);
DEF_R_5 : unsigned(31 downto 0) := str2u("00000007",32);
DEF_R_6 : unsigned(31 downto 0) := str2u("00000002",32);
DEF_R_Z : unsigned(31 downto 0) := str2u("00000000",32) -- Value returned for nonexistent registers
);
port (
 
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Bus interface
adr_i : in unsigned(3 downto 0);
sel_i : in std_logic;
we_i : in std_logic;
dat_i : in unsigned(31 downto 0);
dat_o : out unsigned(31 downto 0);
ack_o : out std_logic;
 
-- Pulse per second time reference
pps_i : in std_logic;
pps_ext_i : in std_logic;
 
-- IRIG select inputs
-- Active only when register 0xC set to zero.
rate_i : in unsigned(2 downto 0); -- Rates : 0,1=>B, 2=>A, 3=>G, 4=> NASA 36-bit
carrier_i : in unsigned(2 downto 0); -- Carriers : 0=DCLS, 1=100Hz, 2=1kHz, 3=10kHz, 4=100kHz
codes_i : in unsigned(2 downto 0); -- Codes : (2)=> control, (1)=> SBS, (0)=> year
 
-- serial IRIG time stream, LVTTL level output
time_dcl_o : out std_logic;
cap_gnd_o : out unsigned(2 downto 0);
ref_o : out std_logic;
carrier_o : out unsigned(1 downto 0)
);
end irig_time_generator;
 
 
architecture beh of irig_time_generator is
 
-- Constants
constant DDS_WIDTH : integer := 32;
constant INCREMENT_600KHZ : unsigned(DDS_WIDTH-1 downto 0) := to_unsigned(integer(2.0**DDS_WIDTH/SYS_CLK_RATE*600000.0),DDS_WIDTH);
constant INCREMENT_510KHZ : unsigned(DDS_WIDTH-1 downto 0) := to_unsigned(integer(2.0**DDS_WIDTH/SYS_CLK_RATE*510000.0),DDS_WIDTH);
constant INCREMENT_501KHZ : unsigned(DDS_WIDTH-1 downto 0) := to_unsigned(integer(2.0**DDS_WIDTH/SYS_CLK_RATE*501000.0),DDS_WIDTH);
constant INCREMENT_500100HZ : unsigned(DDS_WIDTH-1 downto 0) := to_unsigned(integer(2.0**DDS_WIDTH/SYS_CLK_RATE*500100.0),DDS_WIDTH);
 
constant DIVISOR_PPS : real := 1000000.0;
constant DIVISOR_1MHZ : real := SYS_CLK_RATE/DIVISOR_PPS;
constant DIV_COUNT_WIDTH : natural := bit_width(DIVISOR_1MHZ);
constant PPS_COUNT_WIDTH : natural := bit_width(DIVISOR_PPS);
constant SQF_BITS : natural := 4; -- square_fast frequency is Fsys_clk/2^(SQF_BITS+1)
 
-- Internal signal declarations
-- State Machine
type FSM_STATE_TYPE is (RESET, IDLE, P0_REF, PR_REF, P0_DOUT, P1_PLUS_REF, P1_PLUS_DOUT);
signal fsm_state : FSM_STATE_TYPE;
signal fsm_ack : std_logic;
 
-- Related to SBS calculation
-- SBS Calculation State
type SBS_STATE_TYPE is (RESET, IDLE, TIMEKEEPING_DELAY, SBS_10H, SBS_1H, SBS_10M, SBS_1M, SBS_10S);
signal sbs_state : SBS_STATE_TYPE;
 
-- Related to Registers
 
signal leap_year : std_logic;
 
signal secs_0 : unsigned(3 downto 0);
signal secs_1 : unsigned(2 downto 0);
signal mins_0 : unsigned(3 downto 0);
signal mins_1 : unsigned(2 downto 0);
signal hours_0 : unsigned(3 downto 0);
signal hours_1 : unsigned(1 downto 0);
signal days_0 : unsigned(3 downto 0);
signal days_1 : unsigned(3 downto 0);
signal days_2 : unsigned(1 downto 0);
signal years_0 : unsigned(3 downto 0);
signal years_1 : unsigned(3 downto 0);
signal tenths : unsigned(3 downto 0);
signal hundredths : unsigned(3 downto 0);
signal micros : unsigned(13 downto 0); -- Microsecond count
 
signal sbs : unsigned(16 downto 0);
signal sbs_next : unsigned(16 downto 0);
signal sbs_add_count : unsigned(3 downto 0);
 
signal control : unsigned(44 downto 0);
signal reg_rate : unsigned(2 downto 0);
signal reg_carrier : unsigned(2 downto 0);
signal reg_codes : unsigned(2 downto 0);
signal irig_rate : unsigned(2 downto 0);
signal irig_carrier : unsigned(2 downto 0);
signal irig_codes : unsigned(2 downto 0);
 
-- Related to pulse per second and 1MHz clock enable
signal clk_en_1mhz : std_logic;
signal clk_div_count : unsigned(DIV_COUNT_WIDTH-1 downto 0);
signal pps : std_logic;
signal pps_local : std_logic;
signal pps_count : unsigned(PPS_COUNT_WIDTH-1 downto 0);
 
-- Related to generation of the output pulse stream
signal pulse_clk_count : unsigned(9 downto 0); -- Divides by factor of 10, 100 or 1000
signal pulse_clk_en : std_logic;
signal pulse_sub_count : unsigned(3 downto 0); -- Advances when pulse_clk_en=1
signal sub_count_nine : std_logic;
signal p_count : unsigned(3 downto 0); -- Advances when sub_count=9 and pulse_sr_count=9
signal cycle_count : unsigned(6 downto 0); -- Counts output cycles during the current second.
signal pulse_sr : unsigned(8 downto 0);
signal pulse_sr_count : unsigned(3 downto 0); -- Advances when sub_count=9
signal sr_load_bus : unsigned(8 downto 0);
signal p0 : unsigned(8 downto 0); -- sr load value
signal p1 : unsigned(8 downto 0); -- sr load value
signal p2 : unsigned(8 downto 0); -- sr load value
signal p3 : unsigned(8 downto 0); -- sr load value
signal p4 : unsigned(8 downto 0); -- sr load value
signal p5 : unsigned(8 downto 0); -- sr load value
signal p6 : unsigned(8 downto 0); -- sr load value
signal p7 : unsigned(8 downto 0); -- sr load value
signal p8 : unsigned(8 downto 0); -- sr load value
signal p9 : unsigned(8 downto 0); -- sr load value
signal pulse_extent : unsigned(3 downto 0);
signal time_pulses : std_logic;
 
-- Related to generation of the sinewave carrier
signal dds_sync : std_logic;
signal sqf_count : unsigned(SQF_BITS-1 downto 0);
signal square_fast : std_logic;
signal square_500khz : std_logic;
signal heterodyne : std_logic;
signal modulated_carrier : std_logic;
signal phase_dds : unsigned(DDS_WIDTH-1 downto 0);
signal increment_dds : unsigned(DDS_WIDTH-1 downto 0);
 
-----------------------------------------------------------------------------
begin
 
--------------------------
-- Clock enable generation
-- This creates output pulses at 1MHz rate, for use by the IRIG time generator
-- It also creates a 1 Hz "pps" signal in lieu of such a signal from a GPS reference.
clk_en_1mhz_proc: Process(sys_rst_n,sys_clk)
begin
if (sys_rst_n = '0') then
clk_div_count <= (others=>'0');
-- pps_count <= (others=>'0');
pps_count <= to_unsigned(integer(DIVISOR_PPS-200.0),pps_count'length); -- For better simulation
clk_en_1mhz <= '0';
pps_local <= '0';
elsif (sys_clk'event AND sys_clk='1') then
if (sys_clk_en='1') then
clk_en_1mhz <= '0'; -- Default
pps_local <= '0'; -- Default
if (clk_div_count=integer(DIVISOR_1MHZ-1.0)) then
clk_div_count <= (others=>'0');
clk_en_1mhz <= '1';
if (pps_count=integer(DIVISOR_PPS-1.0)) then
pps_count <= (others=>'0');
pps_local <= '1';
else
pps_count <= pps_count+1;
end if;
else
clk_div_count <= clk_div_count+1;
end if;
end if; -- sys_clk_en
end if; -- sys_clk
end process;
-- Select which PPS signal to use
pps <= pps_local when pps_ext_i='0' else pps_i;
 
-- Select which settings to use
irig_rate <= reg_rate when (reg_rate/=0) else rate_i;
irig_carrier <= reg_carrier when (reg_rate/=0) else carrier_i;
irig_codes <= reg_codes when (reg_rate/=0) else codes_i;
 
-- Register read mux
with (adr_i) select
dat_o <=
"000" & leap_year & "0000" & years_1 & years_0 & "000000" & days_2 & days_1 & days_0 when "0000", -- "0";
"0000000000" & hours_1 & hours_0 & "0" & mins_1 & mins_0 & "0" & secs_1 & secs_0 when "0001", -- "1";
control(31 downto 0) when "0010", -- "2";
"0000000000000000000" & control(44 downto 32) when "0011", -- "3";
u_resize(reg_rate,32) when "0100", -- "4";
u_resize(reg_carrier,32) when "0101", -- "5";
u_resize(reg_codes,32) when "0110", -- "6";
DEF_R_Z when others; -- Default
 
-- Create acknowledge signal
ack_o <= '1' when (sel_i='1' and we_i='0') else fsm_ack;
 
 
--------------------------
-- Heterodyning Signals
-- The idea here is to multiply or "mix" two "squarewave" sequences
-- by using an XOR gate, to produce a sum and difference frequency.
-- The sum frequency should be easily filtered out by an RC filter
-- external to the FPGA.
proc_square: Process(sys_rst_n,sys_clk)
begin
if (sys_rst_n = '0') then
square_fast <= '0';
sqf_count <= (others=>'0');
elsif (sys_clk'event AND sys_clk='1') then
if (sys_clk_en='1') then
sqf_count <= sqf_count+1;
if (sqf_count=0) then
square_fast <= not square_fast;
end if;
end if;
end if;
end process;
proc_square_500khz: Process(sys_rst_n,sys_clk)
begin
if (sys_rst_n = '0') then
square_500khz <= '0';
elsif (sys_clk'event AND sys_clk='1') then
if (sys_clk_en='1') then
if (clk_en_1mhz='1') then
square_500khz <= not square_500khz;
end if;
end if;
end if;
end process;
proc_dds: Process(sys_rst_n,sys_clk)
begin
if (sys_rst_n = '0') then
phase_dds <= (phase_dds'length-2=>'1',others=>'0'); -- Set to 1/4 phase point (sinewave "zero" output)
elsif (sys_clk'event AND sys_clk='1') then
if (sys_clk_en='1') then
if (dds_sync='1') then
phase_dds <= (phase_dds'length-2=>'1',others=>'0'); -- Set to 1/4 phase point (sinewave "zero" output)
else
phase_dds <= phase_dds + increment_dds;
end if;
end if; -- sys_clk_en
end if; -- sys_clk
end process;
increment_dds <= INCREMENT_600KHZ when irig_carrier=4 else
INCREMENT_510KHZ when irig_carrier=3 else
INCREMENT_501KHZ when irig_carrier=2 else
INCREMENT_500100HZ;
heterodyne <= square_500khz xor phase_dds(31); -- A sort of digital signal multiplication
modulated_carrier <= heterodyne when time_pulses='1' else square_fast; -- Output midpoint when pulse is low
carrier_o(0) <= '0' when irig_carrier=0 else heterodyne;
carrier_o(1) <= time_pulses when irig_carrier=0 else
modulated_carrier;
ref_o <= '0' when irig_carrier=0 else square_fast; -- Used as a "midpoint" voltage reference
 
--------------------------
-- Setting for output filter capacitor.
-- The idea here is to ground the particular capacitor needed to
-- low pass filter the output. Three different outputs are provided.
cap_gnd_o <= "ZZZ" when irig_carrier=0 or irig_carrier=1 else
"Z00" when irig_carrier=2 else -- Two capacitors in parallel
"Z0Z" when irig_carrier=3 else
"0ZZ";
 
-- This process is where the different BCD values are compiled into
-- a "straight binary seconds" (SBS) field.
-- The calculations are done in a multiply accumulate fashion (MAC)
-- wherein the multiply is implemented as a series of additions.
-- An SBS accumulator stores the intermediate calculation total,
-- while a state variable tracks the current stage of calculation,
-- beginning at "tens of hours" and finishing at "seconds."
-- All the adds performed here are unsigned...
sbs_proc : process(sys_clk,sys_rst_n)
begin
if (sys_rst_n='0') then
sbs_state <= RESET;
sbs <= (others=>'0');
sbs_next <= (others=>'0');
sbs_add_count <= (others=>'0');
elsif (sys_clk'event and sys_clk='1') then
if (sys_clk_en='1') then
case (sbs_state) is
when RESET =>
sbs_state <= IDLE;
when IDLE =>
if (pps='1') then
sbs_state <= TIMEKEEPING_DELAY;
end if;
-- This state waits for changes to the BCD time values to propagate,
-- so they will be stable before this process begins calculation of
-- the SBS field.
when TIMEKEEPING_DELAY =>
sbs <= (others=>'0'); -- Clear accumulator
sbs_next <= u_resize(hours_1,sbs'length);
sbs_add_count <= "1010"; -- Multiply by 10
sbs_state <= SBS_10H;
-- The following series of states performs multiply and accumulate
-- calculations to generate the SBS value.
when SBS_10H =>
if (sbs_add_count=0) then
sbs <= (others=>'0');
sbs_next <= sbs + u_resize(hours_0,sbs'length);
sbs_add_count <= "0110"; -- Multiply by 6
sbs_state <= SBS_1H;
else
sbs <= sbs + sbs_next;
sbs_add_count <= sbs_add_count-1;
end if;
when SBS_1H =>
if (sbs_add_count=0) then
sbs <= (others=>'0');
sbs_next <= sbs + u_resize(mins_1,sbs'length);
sbs_add_count <= "1010"; -- Multiply by 10
sbs_state <= SBS_10M;
else
sbs <= sbs + sbs_next;
sbs_add_count <= sbs_add_count-1;
end if;
when SBS_10M =>
if (sbs_add_count=0) then
sbs <= (others=>'0');
sbs_next <= sbs + u_resize(mins_0,sbs'length);
sbs_add_count <= "0110"; -- Multiply by 6
sbs_state <= SBS_1M;
else
sbs <= sbs + sbs_next;
sbs_add_count <= sbs_add_count-1;
end if;
when SBS_1M =>
if (sbs_add_count=0) then
sbs <= (others=>'0');
sbs_next <= sbs + u_resize(secs_1,sbs'length);
sbs_add_count <= "1010"; -- Multiply by 10
sbs_state <= SBS_10S;
else
sbs <= sbs + sbs_next;
sbs_add_count <= sbs_add_count-1;
end if;
when SBS_10S =>
if (sbs_add_count=0) then
sbs <= sbs + u_resize(secs_0,sbs'length); -- Add seconds (no more multiplication.)
sbs_state <= IDLE;
else
sbs <= sbs + sbs_next;
sbs_add_count <= sbs_add_count-1;
end if;
when others =>
null;
end case;
end if; -- sys_clk_en
end if; -- sys_clk
end process;
 
-- sr load values
p0 <= "00000" & secs_0 when (irig_rate=4) else
"0" & secs_1 & "0" & secs_0;
p1 <= "000000" & secs_1 when (irig_rate=4) else
"0" & mins_1 & "0" & mins_0;
p2 <= "00000" & mins_0 when (irig_rate=4) else
"00" & hours_1 & "0" & hours_0;
p3 <= "000000" & mins_1 when (irig_rate=4) else
days_1 & "0" & days_0;
p4 <= "00000" & hours_0 when (irig_rate=4) else
"0000000" & days_2 when (irig_rate=0 or irig_rate=1) else
tenths & "000" & days_2;
p5 <= "0000000" & hours_1 when (irig_rate=4) else
control(8 downto 4) & hundredths when (irig_rate=3) else
years_1 & '0' & years_0 when (irig_codes(2)='1') else
control(8 downto 0) when (irig_codes(0)='1') else
"000000000";
p6 <= "00000" & days_0 when (irig_rate=4) else
years_1 & '0' & years_0 when (irig_rate=3 and irig_codes(2)='1') else
control(17 downto 9) when (irig_codes(0)='1') else
"000000000";
p7 <= "00000" & days_1 when (irig_rate=4) else
control(26 downto 18) when (irig_codes(0)='1') else
"000000000";
p8 <= "0000000" & days_2 when (irig_rate=4) else
sbs(8 downto 0) when (irig_codes(1)='1') else
control(35 downto 27) when (irig_codes(0)='1') else
"000000000";
p9 <= "111110000" when (irig_rate=4) else
'0' & sbs(16 downto 9) when (irig_codes(1)='1') else
control(44 downto 36) when (irig_codes(0)='1') else
"000000000";
 
-- sr_load_bus mux
with (p_count) select
sr_load_bus <=
p0 when "0000", -- "0";
p1 when "0001", -- "1";
p2 when "0010", -- "2";
p3 when "0011", -- "3";
p4 when "0100", -- "4";
p5 when "0101", -- "5";
p6 when "0110", -- "6";
p7 when "0111", -- "7";
p8 when "1000", -- "8";
p9 when "1001", -- "9";
"000000000" when others; -- Not needed for synthesis...
 
-- Generate a clock enable for advancing the output carrier state
-- The rate of this pulse is determined by the selected IRIG rate.
-- The rate has been selected to allow 10 digital samples per cycle
-- of carrier output.
--
-- Selected Carrier carrier_clk rate Carrier Frequency
-- ---------------- ---------------- -----------------
-- 000 DC Level -None-
-- 001 1 kHz 100 Hz
-- 010 10 kHz 1 kHz
-- 011 100 kHz 10 kHz
-- 100 1 MHz 100 kHz
--
-- Generate a clock enable for advancing the output pulse state
-- The rate of this pulse is determined by the selected IRIG rate.
-- The rate has been selected to allow output pulse duty cycle
-- to be set in increments of 1/10th the total pulse time.
--
-- Selected Rate pulse_clk rate Pulse Frequency
-- ------------- -------------- -----------------
-- 000 1 kHz 100 Hz
-- 001 (B) 1 kHz 100 Hz
-- 010 (A) 10 kHz 1 kHz
-- 011 (G) 100 kHz 10 kHz
-- 100 (NASA 36) 1 kHz 100 Hz
 
pulse_clk_proc : process(sys_clk,sys_rst_n)
begin
if (sys_rst_n='0') then
pulse_clk_count <= (others=>'0');
elsif (sys_clk'event and sys_clk='1') then
if (sys_clk_en='1') then
if (pulse_clk_en='1') then
pulse_clk_count <= (others=>'0');
elsif (clk_en_1mhz='1') then
pulse_clk_count <= pulse_clk_count + 1;
end if;
end if; -- sys_clk_en
end if; -- sys_clk
end process;
pulse_clk_en <= '1' when (clk_en_1mhz='1' and (irig_rate=0 or irig_rate=1 or irig_rate=4) and pulse_clk_count=999) else
'1' when (clk_en_1mhz='1' and irig_rate=2 and pulse_clk_count=99) else
'1' when (clk_en_1mhz='1' and irig_rate=3 and pulse_clk_count=9) else
'0';
 
-- This is the state machine.
-- It handles updates to the timekeeping registers, based on the "pps" signal,
-- and creates the output pulse stream.
fsm_proc: process(sys_clk, sys_rst_n)
begin
if (sys_rst_n='0') then
fsm_state <= RESET; -- asynchronous reset
leap_year <= '0';
micros <= (others=>'0');
hundredths <= (others=>'0');
tenths <= (others=>'0');
years_1 <= DEF_R_0(23 downto 20);
years_0 <= DEF_R_0(19 downto 16);
days_2 <= DEF_R_0(9 downto 8);
days_1 <= DEF_R_0(7 downto 4);
days_0 <= DEF_R_0(3 downto 0);
hours_1 <= DEF_R_1(21 downto 20);
hours_0 <= DEF_R_1(19 downto 16);
mins_1 <= DEF_R_1(14 downto 12);
mins_0 <= DEF_R_1(11 downto 8);
secs_1 <= DEF_R_1(6 downto 4);
secs_0 <= DEF_R_1(3 downto 0);
control <= DEF_R_3(12 downto 0) & DEF_R_2;
reg_rate <= DEF_R_4(2 downto 0);
reg_carrier <= DEF_R_5(2 downto 0);
reg_codes <= DEF_R_6(2 downto 0);
 
p_count <= (others=>'0');
pulse_sr <= (others=>'0');
pulse_sr_count <= to_unsigned(1,pulse_sr_count'length); -- To avoid sending out an initial stream of reference pulses while waiting for pps_local
pulse_sub_count <= (others=>'0');
cycle_count <= (others=>'0');
fsm_ack <= '0';
dds_sync <= '0';
 
elsif (sys_clk'event and sys_clk='1') then
if (sys_clk_en='1') then
 
-- Default values
fsm_ack <= '0';
dds_sync <= '0';
 
-- Handle timekeeping for fractions of seconds, based on 1MHz clock enable.
-- These updates are overridden by pps, which resets all the time count values.
if (clk_en_1mhz='1') then
if (micros=9999) then
micros <= (others=>'0');
if (hundredths=9) then
tenths <= tenths+1;
hundredths <= (others=>'0');
if (tenths=9) then
-- No need to increment seconds here. pps does that.
tenths <= (others=>'0');
else
tenths <= tenths+1;
end if;
else
hundredths <= hundredths+1;
end if;
else
micros <= micros+1;
end if;
end if;
 
-- Handle the timekeeping items, based on pps
if (pps='1') then
secs_0 <= secs_0+1;
tenths <= (others=>'0');
hundredths <= (others=>'0');
micros <= (others=>'0');
if (secs_0=9) then
secs_1 <= secs_1+1;
secs_0 <= (others=>'0');
if (secs_1=5) then
mins_0 <= mins_0+1;
secs_1 <= (others=>'0');
if (mins_0=9) then
mins_1 <= mins_1+1;
mins_0 <= (others=>'0');
if (mins_1=5) then
hours_0 <= hours_0+1;
mins_1 <= (others=>'0');
if (hours_1=2 and hours_0=3) then
days_0 <= days_0+1;
hours_1 <= (others=>'0');
hours_0 <= (others=>'0');
if (days_2>=3 and days_1>=6 and ((days_0>=5 and leap_year='0') or (days_0>=6 and leap_year='1'))) then
years_0 <= years_0+1;
days_0 <= "0001";
days_1 <= (others=>'0');
days_2 <= (others=>'0');
if (years_0=9) then
years_1 <= years_1+1;
years_0 <= (others=>'0');
if (years_1=9) then
years_1 <= (others=>'0');
end if;
end if;
elsif (days_0=9) then
days_1 <= days_1+1;
days_0 <= (others=>'0');
if (days_1=9) then
days_2 <= days_2+1;
days_1 <= (others=>'0');
end if;
end if;
elsif (hours_0=9) then
hours_1 <= hours_1+1;
hours_0 <= (others=>'0');
end if;
end if;
end if;
end if;
end if;
elsif (sel_i='1' and we_i='1') then -- Handle bus writes to registers
-- Cannot do this during pps, since this only writes one value, while
-- pps timekeeping can potentially affect all the values. Therefore,
-- generate an acknowledge signal for the register writes... so that
-- incoming bus cycles are extended when they collide with pps...
fsm_ack <= '1';
case (adr_i) is
when "0000" =>
leap_year <= dat_i(28);
years_1 <= dat_i(23 downto 20);
years_0 <= dat_i(19 downto 16);
days_2 <= dat_i(9 downto 8);
days_1 <= dat_i(7 downto 4);
days_0 <= dat_i(3 downto 0);
when "0001" =>
hours_1 <= dat_i(21 downto 20);
hours_0 <= dat_i(19 downto 16);
mins_1 <= dat_i(14 downto 12);
mins_0 <= dat_i(11 downto 8);
secs_1 <= dat_i(6 downto 4);
secs_0 <= dat_i(3 downto 0);
when "0010" =>
control(31 downto 0) <= dat_i;
when "0011" =>
control(44 downto 32) <= dat_i(12 downto 0);
when "0100" =>
reg_rate <= dat_i(2 downto 0);
when "0101" =>
reg_carrier <= dat_i(2 downto 0);
when "0110" =>
reg_codes <= dat_i(2 downto 0);
when others => null;
end case;
end if;
 
-- Handle pulse_sub_count
if (pulse_clk_en='1') then
if (sub_count_nine='1') then
pulse_sub_count <= (others=>'0');
dds_sync <= '1'; -- Set the carrier wave phase.
else
pulse_sub_count <= pulse_sub_count + 1;
end if;
end if;
 
-- Handle state transitions
case (fsm_state) is
 
when RESET =>
fsm_state <= IDLE;
 
when IDLE =>
if (pps='1') then
fsm_state <= P0_REF;
p_count <= (others=>'0');
cycle_count <= (others=>'0');
pulse_sr_count <= (others=>'0');
end if;
 
when P0_REF =>
if (pulse_clk_en = '1' and sub_count_nine='1') then
pulse_sr_count <= pulse_sr_count+1;
pulse_sr <= sr_load_bus;
if (irig_rate=4) then
fsm_state <= P0_DOUT; -- No special reference pulse for IRIG 36-bit
else
fsm_state <= PR_REF;
end if;
end if;
 
when PR_REF =>
if (pulse_clk_en = '1' and sub_count_nine='1') then
pulse_sr_count <= pulse_sr_count+1;
fsm_state <= P0_DOUT;
end if;
 
when P0_DOUT =>
if (pulse_clk_en = '1' and sub_count_nine='1') then
if (pulse_sr_count=9) then
pulse_sr_count <= (others=>'0');
p_count <= p_count+1;
fsm_state <= P1_PLUS_REF;
else
pulse_sr <= '0' & pulse_sr(8 downto 1);
pulse_sr_count <= pulse_sr_count+1;
end if;
end if;
 
when P1_PLUS_REF =>
if (pulse_clk_en = '1' and sub_count_nine='1') then
pulse_sr_count <= pulse_sr_count+1;
pulse_sr <= sr_load_bus;
fsm_state <= P1_PLUS_DOUT;
end if;
 
when P1_PLUS_DOUT =>
if (p_count=9 and pps='1') then -- pps should not occur before p_count=9 anyway...
fsm_state <= P0_REF;
p_count <= (others=>'0');
cycle_count <= (others=>'0');
pulse_sr_count <= (others=>'0');
elsif (pulse_clk_en = '1' and sub_count_nine='1') then
if (pulse_sr_count=9) then
if (p_count=9) then
if (cycle_count=99 and irig_rate=3) then
fsm_state <= IDLE;
elsif (cycle_count=9 and irig_rate=2) then
fsm_state <= IDLE;
elsif (irig_rate=0 or irig_rate=1 or irig_rate=4) then
fsm_state <= IDLE;
else
cycle_count <= cycle_count+1;
p_count <= (others=>'0');
pulse_sr_count <= (others=>'0');
fsm_state <= P0_REF;
end if;
else
pulse_sr_count <= (others=>'0');
p_count <= p_count+1;
pulse_sr <= sr_load_bus;
fsm_state <= P1_PLUS_REF;
end if;
else
pulse_sr <= '0' & pulse_sr(8 downto 1);
pulse_sr_count <= pulse_sr_count+1;
end if;
end if;
 
--when others =>
-- fsm_state <= IDLE;
end case;
 
end if; -- sys_clk_en
end if; -- sys_clk
end process;
 
sub_count_nine <= '1' when (pulse_sub_count=9) else '0';
 
-- Form output pulses
pulse_extent <= "0010" when (irig_rate=4 and pulse_sr_count=0 and p_count=0) else
"0101" when (irig_rate=4 and pulse_sr_count=0) else
"1000" when (pulse_sr_count=0 or fsm_state=PR_REF) else
"0101" when (pulse_sr(0)='1') else
"0010";
 
time_pulses <= '1' when (pulse_sub_count<pulse_extent) else '0';
time_dcl_o <= time_pulses;
 
end beh;
 
-------------------------------------------------------------------------------
-- IRIG time pattern receiver-generator core
-------------------------------------------------------------------------------
--
-- Author: John Clayton
-- Date : Apr. 4, 2012 Copied code from irig_time_generator. Updated
-- description.
-- Oct. 12, 2012 Added read only status registers, that allow
-- reporting of the lock condition, as well as the
-- "slow_err" and "fast_err" signals. These were
-- not being used, and were being optimized away
-- ("pruned") at synthesis time.
-- Apr. 30, 2013 Changed SYS_CLK_RATE from integer to real type.
-- June 12, 2013 Changed low pass filter circuit topology, reducing
-- the number of "cap_gnd_o" lines. Increased the
-- heterodyne input frequency from 500kHz to 2MHz.
-- Oct. 10, 2013 Added the cdet_sel_o line to activate carrier
-- detect circuitry by register control. Added leaky
-- integrator based lowpass filter from
-- "signal_conditioning_pack.vhd"
-- Oct. 14, 2013 Cleaned up the design, changing the heterodyne
-- from 2MHz to 1MHz, eliminating "square_fast" and
-- using the 1MHz squarewave instead. Eliminated
-- rx_phase and rx_dds_r1 by using "dds_squarewave"
-- module. Changed rx_phase_incr -> rx_freq,
-- rx_dds_rising -> rx_1mhz_pulse. Eliminated
-- "phase_dds" and "increment_dds" by using
-- dds_squarewave_phase_load module.
-- Nov. 6, 2013 Divided receive and generate logic into separate
-- processes. removed rx_lock_state state machine.
-- Eliminated rx_frame_count. Added rx_c_count,
-- gave priority to external PPS tracking over
-- carrier count PPS (IRIG) tracking. Added
-- hdyne_base_square so that modulation frequency
-- is not "warped" or affected by the tracking
-- mechanism. Added "set_time" signal. Removed
-- rx_fast_err and rx_slow_err. Removed rx_speed_sense.
-- Dec. 6, 2013 Moved timecode gated pulse stretcher to a higher level,
-- and introduced time_cdet_i. Now, "time_dcl_i" really
-- will be a DC-level timecode input, and "time_cdet_i"
-- is a squarewave at the carrier rate, during times
-- when cdet_sel_o is high. Removed the
-- "rx_pulse_stretcher" register bit.
-- Dec. 9, 2013 Removed the time_dcl_i input, in favor of using the
-- time_cdet_i input always. Added reg_time_lp lowpass
-- filter enable bit.
-- Dec. 10, 2013 Revised the data detection logic to use "rx_lo_count"
-- and tested in hardware. All seems to be working!
-- O, happy day!
-- Dec. 11, 2013 Added rx_active_count to separate rx activity detect
-- from the data receive counter "rx_lo_count." This
-- allows activity detect to be on edges, while the
-- data receive is on levels, thus allowing reception of
-- true dc-level timecode input data.
-- Nov. 14, 2014 Reduced cdet_val from 4 bits down to 2 bits, and
-- provided it as an output signal for inclusion in
-- a status stream.
-- Mar. 11, 2015 Added leap_year bit to register zero. Prevented
-- day values higher than 366 from persisting.
--
-- Description
-------------------------------------------------------------------------------
-- This time pattern receiver is also a time pattern generator.
-- More correctly, it is a time pattern generator which synchronizes itself to
-- an incoming IRIG time signal. Also, it provides parallel output of the
-- current time so that timestamps may be taken when telemetered data is being
-- sent to an archive.
--
-- Note that during playback, "one time" syncronization is used. The IRIG
-- time is set once, at the start of the playback, to match the first minor
-- frame timestamp. Thereafter, the freewheeling quartz crystal based
-- IRIG time is sent out along with the playback data. This is necessary
-- because there is no feasible way to simultaneously adjust the IRIG time
-- and the data playback rate so as to keep them both matched to some outside
-- time reference, without introducing discontinuities in the IRIG time
-- signal.
--
-- The incoming IRIG time signal is applied to a pulse period meter, which is
-- built using a binary counter. As the period is measured, decisions are
-- made about what type of bit is being encountered. A period close to 20
-- counts is considered a zero, close to 50 counts, a one, and close to 80
-- counts, a "reference" pulse. The counter is clocked by a pre-scaler so
-- that it can measure the period in units of 100, 10 or 1 microsecond, for
-- IRIG types B, A and G, respectively. For rapid acquisition of all types,
-- the default is to use units of 1 microsecond (type G.) If the count
-- exceeds 85, then the prescaler is adjusted to provide units of 10
-- microseconds (type A). If, once again, the count exceeds 85, then the
-- prescaler is adjusted to use units of 100 microseconds (type B.) Since
-- type B is the slowest supported type, if the count once again exceeds 85
-- counts, then a "slow" error condition is flagged.
--
-- Similar converse adjustments are made for period measurements less than
-- 10 counts. If pulses of under 10 microseconds are found, then a "fast"
-- error condition is flagged.
--
-- This unit is set up to receive DC Level data, and no attempt is made to
-- use the modulating carrier for timing reference information.
--
-- The unit outputs DC Level data, plus a pair of digital outputs which
-- contain a modulated carrier signal. The modulated carrier frequency is the
-- low-frequency heterodyne signal generated by digital mixing of a 1 MHz
-- squarewave with a 1001 kHz (IRIG "B"), 1010 kHz (IRIG "A") or 1600 kHz
-- (IRIG "G") squarewave, as generated by a DDS. As such, the digital output
-- needs to be low-pass filtered in order to cut down the amplitude of the
-- high-frequency components.
--
-- In order to modulate the amplitude of the resulting low-pass filtered
-- carrier over a 3:1 ratio (An AM modulation index of 0.5, meaning 50% modulation
-- depth) a 2-bit R-2R resistor ladder network DAC is recommended for use outside
-- the FPGA. Also, in order to keep the modulated signal balanced, a reference
-- voltage other than ground is used. The reference voltage is nominally 1/2 of
-- the FPGA VCCo supply voltage (3.3V * 0.5 = 1.65V). The FPGA generates this
-- reference voltage through the use of an output which provides a 50% duty cycle
-- squarewave. The schematic is as follows:
--
-- R 1.65V R R
-- ref_o o---/\/\--.--/\/\--/\/\--.
-- | |
-- Cref - + |
-- 15uF - |
-- | |
-- GND |
-- R R |
-- carrier_o(0) o------------/\/\--/\/\--.
-- (LSB) |
-- R <
-- < Cout
-- R R | 4.7uF
-- carrier_o(1) o------------/\/\--/\/\--.-----|(-------o AC coupled, Modulated Output
-- (MSB) |
-- Cfilt |
-- 0.1uF |
-- cap_gnd_o o------.-----|(----------.
-- |
-- .-------------.
-- |
-- Cfilt |
-- 0.01uF |
-- GND o------------|(------.
--
-- In practice, R=270 ohms has been successfully used.
-- The Cfilt values are not exact, but the values shown worked well enough.
--
-- The two carrier signals are identical, except that the LSB is constantly driving
-- the heterodyne signal out, while the MSB is only driving the heterodyne signal
-- during times when the irig DC level pulse is high, otherwise is outputs a 50%
-- duty cycle signal, similar to ref_o. The cap_gnd_o outputs serves to adjust
-- the corner frequency of the RC low pass filter. They are not pulsed with any
-- high frequency signals. They simply apply ground or else high impedance,
-- thereby effectively bringing the Cfilt capacitors in and out of the circuit.
--
-- It is recommended to use the Cout capacitor in series with the output to provide
-- AC coupling of the signal, and some DC isolation for the FPGA.
--
-- This core uses a 1MHz clock enable pulse, to count the passage of time
-- and output a pulse stream which conforms to IRIG standard 200-04, for the
-- following IRIG formats:
--
-- First letter: B (100 pps), A (1000 pps) or G (10000 pps)
-- First digit : 0 = DC Level shift output (no modulation)
-- Second digit : 0 = No carrier
-- Third digit : [0..7] (BCD plus combinations of Year, CF and SBS fields)
--
-- The core generates its own 1 MHz clock enable by using an NCO, with a phase
-- accumulator register whose size is set by generics. When locked to an
-- incoming timecode signal, the "integrated residual" (a.k.a. residusum)
-- is checked and adjusted each second in order to "warp" the timebase rate
-- to match the incoming timecode signal. In this way, the timecode
-- regenerator tracks variations in the timing of the incoming IRIG signal.
--
-- When not locked to an incoming timecode signal, there is an alternate method
-- for keeping the time accurate over the longer term, namely the use of
-- the pps_i input, a single pulse per second, detected on its rising edge.
-- The pps_i input may be supplied by an external GPS module for accurate time.
-- In the absence of incoming IRIG time signal or outside GPS reference, the
-- local quartz crystal based system clock is left to "freewheel" on its own,
-- which inevitably results in the buildup of inaccuracies in timekeeping,
-- depending on temperature, aging and physical handling of the quartz crystal
-- itself.
--
-- All settings for the core are contained in read/write registers, which
-- have default values determined by generics. In the case of a "code"
-- selection which includes straight binary seconds ("SBS"), the control
-- bits for the P8 and P9 portions of the output are not used, and the
-- internally calculated SBS field is used instead.
--
-- The year field also displaces control bits when it is selected. The two
-- digit year is fully supported, and will increment properly at the end of
-- 23:59:59 on day 364. (No leap year).
--
-- For rates A and G, there is a tenths of a second BCD digit present in the
-- latter end of the P4 period.
--
-- For rate G, there is a hundredths of a second BCD digit present in the
-- former end of the P5 period.
--
-- If the year is desired, it can be placed in the proper position within
-- the control bits, as shown here...
-- Format B or A :
-- Year_1 (the most significant BCD digit) =>
-- control_1(0) & control_0(7 downto 5)
-- control_0(4) remains '0'
-- Year_0 (the least significant BCD digit) =>
-- control_0(3 downto 0)
--
-- Format G :
-- Year_1 (the most significant BCD digit) =>
-- control_2(1 downto 0) & control_1(7 downto 6)
-- control_1(5) remains '0'
-- Year_0 (the least significant BCD digit) =>
-- control_1(4 downto 1)
--
-- Bus interface writes to the registers cannot be given higher priority
-- than internal updates for timekeeping based on pps_i, since bus writes
-- only affect one value, while timekeeping can occasionally alter all of
-- them. Thus corrupt and undesired behavior could result if bus writes
-- were to override the timekeeping update values. Therefore, the ack_o
-- signal is used to ensure that the timekeeping is finished before bus
-- writes take place.
--
-- The registers are summarized as follows:
--
-- Address Structure Function
-- ------- ---------- ----------------------------------------
-- 0x0 [**YY*DDD] BCD Year, Day of Year (Day is 1..365)
-- 0x1 [**HHMMSS] BCD Hours, Minutes, Seconds. (*=unused nibble)
-- 0x2 [CCCCCCCC] Control Bits (31:0)
-- 0x3 [**CCCCCC] Control Bits (44:32)
-- 0x4 (2:0) Rate Setting (See Notes below)
-- 0x5 (2:0) Carrier Modulation Frequency
-- 0x6 (2:0) Codes Used [ctrl,SBS,year]
-- 0x7 (6:0) Time receiver control and status
-- 0x8 (31:0) Statistical report (read only)
-- 0x9 (31:0) PPS residusum, a 25 bit signed value, (sign extended)
-- 0xA (8:0) Lowpass filter enable, CDET bias setting
--
-- Notes:
--
-- Register 0x0 (Leap Year, Year, Day of Year)
--
-- Bit 28 controls the leap year setting:
-- This register currently has only a single bit for setting leap year
-- behavior. When the leap year bit is set, the day field advances to 366
-- before rolling over back to 1. When the bit is clear, the day field
-- to 1 after the 365 day value. Note that the DDD field can be written
-- with values higher than 366, but it will immediately roll over.
--
-- Register 0x4 (Rate)
-- 000 => (Use external input for Rate/Form/Carrier/Codes)*
-- 001 => IRIG-B (1 second updates)
-- 010 => IRIG-A (0.1 second updates)
-- 011 => IRIG-G (0.01 second updates)
-- 100 => NASA 36-bit (WWV) time code
--
-- The "rate_i" input is used along with the register setting, in such
-- a way that the register Rate value being zero, causes the "rate_i" and
-- "codes_i" inputs to be used. If, however, the register Rate is set to
-- a non-zero value, then the external input is ignored.
--
-- When external rate input is used, a setting of "000" results
-- in IRIG-B (1 second updates.) All other settings remain
-- as outlined above.
--
-- Register 0x5 (Carrier)
--
-- 000 => No carrier (DC Level Shift)
-- 001 => 100 Hz / 10 ms. resolution
-- 010 => 1 kHz / 1 millisecond resolution
-- 011 => 10 kHz / 100 microsecond resolution
-- 100 => 100 kHz / 10 microsecond resolution
--
-- Internal logic takes the day, hour, minute and second values and derives
-- a value for the SBS field (Straight Binary Second).
--
-- Register 0x6 (Codes)
-- Each bit position, when set, activates the use of a specific field
-- within the output, according to the form:
--
-- [ctrl,SBS,year]
--
-- Where ctrl = Control field
-- SBS = Straight Binary Seconds
-- year = Year field
--
-- If year and control are both selected, then the year is used in its
-- appropriate spot, preventing the control bits there from being used.
--
-- For Rate "G", the hundredths of a second field occludes the first four
-- control bits, so that they are not used. The hundredths of a second field
-- is encoded in the register area for "year" since year is not used in
-- Rate "G"
--
-- Register 0x07
-- Time Receiver Status register
-- bits (1:0) = cdet_val. These bits are the carrier detect value,
-- which relates to the detected IRIG timecode speed.
-- cdet_val Meaning
-- -------- -----------------------------
-- 3 IRIG G speed (100kHz carrier)
-- 2 IRIG A speed (10 kHz carrier)
-- 1 IRIG B speed (1 kHz carrier)
-- 0 no carrier detected.
--
-- bits (3:2) = reserved (value is set to "00").
--
-- bit (4) = active status bit. '1' means activity was detected on the
-- irig_dcl_i input.
-- bit (5) = lock status bit. '1' indicates the receiver is in the
-- TRACK_IRIG state
-- bit (6) = IRIG tracking status bit. Read only bit that indicates the
-- received IRIG timecode is valid for time tracking
-- bit (7) = PPS tracking status bit. Read only bit that indicates the
-- received PPS signal is valid for time tracking
-- bit (8) = neverlocked status bit. '1' indicates the unit has not locked
-- since reset. This bit can only be set after Power On Reset.
-- Read only.
-- bit (9) = rx_nasa36 bit. This bit is set whenever NASA 36 timecode is
-- detected and used. Read only.
-- bit (10)= cdet_sel_l bit. This bit is read only, and indicates when
-- the receiver state machine is asserting carrier detect mode.
-- When this bit is set, external circuitry is configured to
-- strip out the time information, and only provide a
-- "zero crossing" detected carrier on the time_cdet_i input.
-- The "zero crossing" detected carrier is a squarewave at the
-- frequency of the carrier. When this bit is clear, pulses arrive
-- corresponding to the times when the carrier is at high-amplitude.
-- bits (31:24) = rx_pulse_width. This is the most recently measured pulse
-- low value as a percentage. Nominal IRIG timecode pulses
-- should be high: 20% for zero, 50% for one, and 80% for ref.
-- The rx_pulse_width is a count of the low time after the
-- trailing edge of the received pulse. Thus, values >96
-- represent pulses which are too short, >72 are zeros,
-- >48 are ones and >16 are ref pulses. These thresholds are
-- defined as constants within the code.
--
-- Register 0x08
-- Receiver additional information
-- bits (3:0) = receiver current state
-- value name meaning
-- ----- ----------- --------------------------------------------------------------
-- 0 SETTLE_DOWN Awaiting capacitor discharge following cdet_sel_o being cleared
-- 1 CDET_1 Awaiting carrier speed detect synchronization
-- 2 CDET_2 Awaiting carrier speed detect
-- 3 SETTLE_UP Awaiting capacitor charge following cdet_sel_o being set
-- 4 SEEK_REF_1 Seeking a longer "reference" type pulse
-- 5 SEEK_REF_2 Seeking a second CONSECUTIVE "reference" type pulse
-- 6 RECEIVE_PULSES Receiving and decoding IRIG time pulses
-- 7 SETTLE_IN Awaiting capacitor discharge heading into TRACK_IRIG
-- 8 TRACK_IRIG Tracking incoming IRIG time carrier based PPS
--
-- bits (7:4) = reserved
--
-- bits (15:8) = number of times rx_active has been asserted. Write to clear.
-- bits (23:16) = number of times RECEIVE_PULSES has been entered. Write to clear.
-- bits (31:24) = number of times TRACK_IRIG has been entered. Write to clear.
--
-- Register 0x09
-- Receiver PPS tracking "residusum"
-- This is a signed quantity which represents the speed adjustment to the internally
-- generated IRIG time output. It is based on measurements against a incoming pulse
-- per second time reference. There are two PPS references: pps_i and an internal
-- irig_pps which is based on dividing down the received carrier frequency whenever
-- the receiver is in the TRACK_IRIG state.
-- This is a signed quantity, which has been sign extended to 32 bits. It is limited
-- by the constant RESIDUSUM_BITS to be at most +/- 1/128 the magnitude of the frequency
-- setting (An allowed frequency variation window of +/- 0.7825 percent.)
--
-- Register 0x0A
-- Carrier Detect Bias, and time lowpass filter enable
-- bits (5:0) = Carrier detect bias value. Ranging from 0x00 to 0x20, this value
-- drives a PWM output to an RC filter network, creating a voltage used
-- as a threshold to detect either the timecode carrier, or the carrier
-- "high amplitude peaks" depending on cdet_sel_o.
-- bit (8) = time_cdet_i lowpass filter enable. When set, this bit enables use of
-- a leaky-integrator based lowpass filter.
--
-- The generics used as defaults for the registers are the full width of the
-- data bus. However, only those bits which are actually present in the actual
-- registers are used.
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
 
library work;
use work.dds_pack.all;
use work.pwm_pack.all;
use work.convert_pack.all;
use work.signal_conditioning_pack.all;
 
entity irig_time_regenerator is
generic (
SYS_CLK_RATE : real := 40000000.0; -- Needed for carrier generation
LOCK_THRESHOLD : integer := 8; -- Frames received before entering locked state
DEF_R_0 : unsigned(31 downto 0) := str2u("00100123",32);
DEF_R_1 : unsigned(31 downto 0) := str2u("00000000",32);
DEF_R_2 : unsigned(31 downto 0) := str2u("00000000",32);
DEF_R_3 : unsigned(31 downto 0) := str2u("00000000",32);
DEF_R_4 : unsigned(31 downto 0) := str2u("00000001",32);
DEF_R_5 : unsigned(31 downto 0) := str2u("00000002",32);
DEF_R_6 : unsigned(31 downto 0) := str2u("00000007",32);
DEF_R_A : unsigned(31 downto 0) := str2u("00000014",32); -- Initial value of carrier detect bias
DEF_R_Z : unsigned(31 downto 0) := str2u("00000000",32) -- Value returned for nonexistent registers
);
port (
 
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Bus interface
adr_i : in unsigned(3 downto 0);
sel_i : in std_logic;
we_i : in std_logic;
dat_i : in unsigned(31 downto 0);
dat_o : out unsigned(31 downto 0);
ack_o : out std_logic;
 
-- Pulse per second time reference
 
-- IRIG select inputs
-- Active only when register 0x4 set to zero.
rate_i : in unsigned(2 downto 0); -- Rates : 0,1=>B, 2=>A, 3=>G, 4=>NASA 36-bit
carrier_i : in unsigned(2 downto 0); -- Carriers : 0=DCLS, 1=100Hz, 2=1kHz, 3=10kHz, 4=100kHz
codes_i : in unsigned(2 downto 0); -- Codes : (2)=> control, (1)=> SBS, (0)=> year
 
-- IRIG time output, parallel form
time_o : out unsigned(67 downto 0);
 
-- Carrier Detect Select output, PPS input,
-- IRIG time input, tracking and lock indicators
cdet_sel_o : out std_logic;
pps_i : in std_logic;
time_cdet_i : in std_logic;
cdet_val_o : out unsigned(1 downto 0);
active_o : out std_logic;
tracking_o : out std_logic;
lock_o : out std_logic;
neverlocked_o : out std_logic;
 
-- Serial IRIG time stream output, LVTTL level
time_dcl_o : out std_logic;
cap_gnd_o : out std_logic;
ref_o : out std_logic;
carrier_o : out unsigned(1 downto 0)
);
end irig_time_regenerator;
 
architecture beh of irig_time_regenerator is
 
-- Constants
 
-- IRIG Generator Constants
-- DDS size
constant RX_1MHZ_DDS_BITS : natural := 32;
constant HDYNE_DDS_BITS : integer := 32;
-- Digital Heterodyne constants
constant INCREMENT_1000KHZ : unsigned(HDYNE_DDS_BITS-1 downto 0) := to_unsigned(integer(2.0**HDYNE_DDS_BITS/SYS_CLK_RATE*1000000.0),HDYNE_DDS_BITS);
constant INCREMENT_1100KHZ : unsigned(HDYNE_DDS_BITS-1 downto 0) := to_unsigned(integer(2.0**HDYNE_DDS_BITS/SYS_CLK_RATE*1100000.0),HDYNE_DDS_BITS);
constant INCREMENT_1010KHZ : unsigned(HDYNE_DDS_BITS-1 downto 0) := to_unsigned(integer(2.0**HDYNE_DDS_BITS/SYS_CLK_RATE*1010000.0),HDYNE_DDS_BITS);
constant INCREMENT_1001KHZ : unsigned(HDYNE_DDS_BITS-1 downto 0) := to_unsigned(integer(2.0**HDYNE_DDS_BITS/SYS_CLK_RATE*1001000.0),HDYNE_DDS_BITS);
constant INCREMENT_1000100HZ : unsigned(HDYNE_DDS_BITS-1 downto 0) := to_unsigned(integer(2.0**HDYNE_DDS_BITS/SYS_CLK_RATE*1000100.0),HDYNE_DDS_BITS);
 
-- IRIG receiver constants
constant FRAME_COUNT_BITS : natural := bit_width(LOCK_THRESHOLD);
constant PHASE_INCR_1MHZ : natural := natural(1000000.0/SYS_CLK_RATE*(2**real(RX_1MHZ_DDS_BITS)));
constant RESIDUSUM_BITS : natural := RX_1MHZ_DDS_BITS-7; -- Restrict variation to approx. 1% (1/128) of frequency setting
constant RX_EDGE_VAL : natural := 16;
constant RX_ONE_VAL : natural := 48;
constant RX_ZERO_VAL : natural := 72;
constant RX_TOO_SHORT : natural := 96;
constant RX_C_SEED_B : integer := 1000; -- Counts carrier pulses
constant RX_C_SEED_A : integer := 10000; -- Counts carrier pulses
constant RX_C_SEED_G : integer := 100000; -- Counts carrier pulses
constant PPS_R_COUNT_SEED : integer := 999999; -- Counts 1 million microseconds...
constant PPS_TRACK_LIMIT : integer := 1000;
constant CDET_SETTLE_TIME : integer := 24; -- Milliseconds of settling when cdet_sel_o changes.
--constant CDET_SETTLE_TIME : integer := 0; -- For fast simulation
-- NOTE: It was discovered during testing that, with the enhancements to the
-- IRIG timecode receiver, settling time is not needed in order to obtain a
-- lock on the incoming signal. Therefore, apart from a slight initial
-- inaccuracy in frequency tracking for <24ms in the TRACK_IRIG state, there
-- is no effect from skipping the settling time. It will be left at zero ms
-- unless a compelling reason is found to increase it.
 
-- Internal signal declarations
-- State Machine
type FSM_STATE_TYPE is (RESET, IDLE, P0_REF, PR_REF, P0_DOUT, P1_PLUS_REF, P1_PLUS_DOUT);
signal fsm_state : FSM_STATE_TYPE;
signal fsm_ack : std_logic;
signal ack_l : std_logic;
 
-- Related to SBS calculation
-- SBS Calculation State
type SBS_STATE_TYPE is (RESET, IDLE, TIMEKEEPING_DELAY, SBS_10H, SBS_1H, SBS_10M, SBS_1M, SBS_10S);
signal sbs_state : SBS_STATE_TYPE;
 
-- Related to Registers
signal leap_year : std_logic;
signal secs_0 : unsigned(3 downto 0);
signal secs_1 : unsigned(2 downto 0);
signal mins_0 : unsigned(3 downto 0);
signal mins_1 : unsigned(2 downto 0);
signal hours_0 : unsigned(3 downto 0);
signal hours_1 : unsigned(1 downto 0);
signal days_0 : unsigned(3 downto 0);
signal days_1 : unsigned(3 downto 0);
signal days_2 : unsigned(1 downto 0);
signal years_0 : unsigned(3 downto 0);
signal years_1 : unsigned(3 downto 0);
signal tenths : unsigned(3 downto 0);
signal hundredths : unsigned(3 downto 0);
 
signal sbs : unsigned(16 downto 0);
signal sbs_next : unsigned(16 downto 0);
signal sbs_add_count : unsigned(3 downto 0);
 
signal control : unsigned(44 downto 0);
signal reg_rate : unsigned(2 downto 0);
signal reg_carrier : unsigned(2 downto 0);
signal reg_codes : unsigned(2 downto 0);
signal irig_rate : unsigned(2 downto 0);
signal irig_carrier : unsigned(2 downto 0);
signal irig_codes : unsigned(2 downto 0);
 
-- Related to generation of the output pulse stream
signal tx_1mhz_pulse : std_logic; -- 1MHz pulse stream which can warp when tracking.
signal set_time : std_logic;
signal pulse_clk_count : unsigned(9 downto 0); -- Divides by factor of 10, 100 or 1000
signal pulse_clk_en : std_logic;
signal pulse_sub_count : unsigned(3 downto 0); -- Advances when pulse_clk_en=1
signal sub_count_nine : std_logic;
signal p_count : unsigned(3 downto 0); -- Advances when sub_count=9 and pulse_sr_count=9
signal cycle_count : unsigned(6 downto 0); -- Counts output cycles during the current second.
signal pulse_sr : unsigned(8 downto 0);
signal pulse_sr_count : unsigned(3 downto 0); -- Advances when sub_count=9
signal sr_load_bus : unsigned(8 downto 0);
signal p0 : unsigned(8 downto 0); -- sr load value
signal p1 : unsigned(8 downto 0); -- sr load value
signal p2 : unsigned(8 downto 0); -- sr load value
signal p3 : unsigned(8 downto 0); -- sr load value
signal p4 : unsigned(8 downto 0); -- sr load value
signal p5 : unsigned(8 downto 0); -- sr load value
signal p6 : unsigned(8 downto 0); -- sr load value
signal p7 : unsigned(8 downto 0); -- sr load value
signal p8 : unsigned(8 downto 0); -- sr load value
signal p9 : unsigned(8 downto 0); -- sr load value
signal pulse_extent : unsigned(3 downto 0);
signal time_pulses : std_logic;
signal pps : std_logic;
 
-- Related to generation of the sinewave carrier
signal hdyne_dds_sync : std_logic;
signal hdyne_base_square : std_logic;
signal hdyne_in_square : std_logic;
signal hdyne_out : std_logic;
signal hdyne_in_freq : unsigned(HDYNE_DDS_BITS-1 downto 0);
signal modulated_carrier : std_logic;
 
-- Related to Timecode Receiver
type RX_STATE_TYPE is (WAIT_ACTIVE, SEEK_REF_1, SEEK_REF_2, RECEIVE_PULSES, SETTLE_IN, TRACK_IRIG);
signal rx_state : RX_STATE_TYPE;
signal rx_nasa36 : std_logic; -- High means NASA 36-bit code detected
signal rx_sr : unsigned(9 downto 0);
signal rx_scale_factor : unsigned(7 downto 0);
signal rx_scale_count : unsigned(7 downto 0);
signal rx_lo_count : unsigned(7 downto 0);
signal rx_pulse_width : unsigned(7 downto 0);
signal rx_pulse_count : unsigned(3 downto 0);
signal rx_group_count : unsigned(3 downto 0);
signal rx_freq : unsigned(RX_1MHZ_DDS_BITS-1 downto 0); -- Sets frequency
signal rx_1mhz_pulse : std_logic;
signal rx_posedge : std_logic;
signal rx_pstart : std_logic;
signal rx_hi_count : unsigned(7 downto 0);
signal rx_raise_count : unsigned(3 downto 0);
signal rx_active_wdog : unsigned(7 downto 0);
signal rx_active_count : unsigned(3 downto 0);
signal rx_active : std_logic;
signal rx_pps_tracking : std_logic;
signal rx_irig_tracking : std_logic;
signal rx_lock : std_logic;
signal micro_bcd_0 : unsigned(3 downto 0);
signal micro_bcd_1 : unsigned(3 downto 0);
signal micro_bcd_2 : unsigned(3 downto 0);
signal micro_bcd_3 : unsigned(3 downto 0);
signal micro_bcd_4 : unsigned(3 downto 0);
signal micro_bcd_5 : unsigned(3 downto 0);
signal rx_c_count : unsigned(16 downto 0);
signal pps_r_count : signed(20 downto 0); -- Counts down 1 sec. worth of microseconds.
signal pps_residusum : signed(RESIDUSUM_BITS-1 downto 0);
signal rx_secs_0 : unsigned(3 downto 0);
signal rx_secs_1 : unsigned(2 downto 0);
signal rx_mins_0 : unsigned(3 downto 0);
signal rx_mins_1 : unsigned(2 downto 0);
signal rx_hours_0 : unsigned(3 downto 0);
signal rx_hours_1 : unsigned(1 downto 0);
signal rx_days_0 : unsigned(3 downto 0);
signal rx_days_1 : unsigned(3 downto 0);
signal rx_days_2 : unsigned(1 downto 0);
signal rx_years_0 : unsigned(3 downto 0);
signal rx_years_1 : unsigned(3 downto 0);
signal rx_neverlocked : std_logic;
signal cdet_fixed : std_logic;
signal cdet_val_l : unsigned(1 downto 0);
signal cdet_sel_l : std_logic;
signal pulse_1ms : std_logic;
signal cdet_ms_timer : unsigned(4 downto 0);
signal cdet_bias : unsigned(5 downto 0);
signal cdet_dac_clk_en : std_logic;
signal time_cleaner_input : signed(15 downto 0);
signal time_cleaned : std_logic;
signal time_rx : std_logic;
signal time_rx_r1 : std_logic;
signal time_rx_r2 : std_logic;
signal reg_time_lp : std_logic;
signal pps_r1 : std_logic;
signal pps_r2 : std_logic;
signal rx_state_report : unsigned(3 downto 0);
signal rx_stat_1 : unsigned(7 downto 0);
signal rx_stat_2 : unsigned(7 downto 0);
signal rx_stat_3 : unsigned(7 downto 0);
 
-----------------------------------------------------------------------------
begin
 
-- Select which settings to use
irig_rate <= reg_rate when (reg_rate/=0) else rate_i;
irig_carrier <= reg_carrier when (reg_rate/=0) else carrier_i;
irig_codes <= reg_codes when (reg_rate/=0) else codes_i;
 
-- Register read mux
with (adr_i) select
dat_o <=
"000" & leap_year & "0000" & years_1 & years_0 & "000000" & days_2 & days_1 & days_0 when "0000", -- "0";
"0000000000" & hours_1 & hours_0 & "0" & mins_1 & mins_0 & "0" & secs_1 & secs_0 when "0001", -- "1";
control(31 downto 0) when "0010", -- "2";
"0000000000000000000" & control(44 downto 32) when "0011", -- "3";
u_resize(reg_rate,32) when "0100", -- "4";
u_resize(reg_carrier,32) when "0101", -- "5";
"00000000000000000000000000000" & reg_codes when "0110", -- "6";
rx_pulse_width & "0000000000000" & cdet_fixed & rx_nasa36 & rx_neverlocked &
rx_pps_tracking & rx_irig_tracking & rx_lock & rx_active & "00" & cdet_val_l when "0111", -- "7";
rx_stat_3 & rx_stat_2 & rx_stat_1 & "0000" & rx_state_report when "1000", -- "8";
unsigned(s_resize_se(pps_residusum,32)) when "1001", -- "9";
"00000000000000000000000" & reg_time_lp & u_resize(cdet_bias,8) when "1010", -- "A";
DEF_R_Z when others; -- Default
 
-- Create acknowledge signal
ack_l <= fsm_ack when (sel_i='1' and we_i='1' and adr_i<7) else -- special ack for tx fsm registers
'1' when (sel_i='1') else
'0';
ack_o <= ack_l;
 
-- Produce parallel output
time_o <= years_1 & years_0 & "00" & days_2 & days_1 & days_0 & "00" & hours_1 & hours_0 &
'0' & mins_1 & mins_0 & '0' & secs_1 & secs_0 & micro_bcd_5 & micro_bcd_4 &
micro_bcd_3 & micro_bcd_2 & micro_bcd_1 & micro_bcd_0;
 
--------------------------
-- Heterodyning Signals
-- The idea here is to multiply or "mix" two "squarewave" sequences
-- by using an XOR gate, to produce a sum and difference frequency.
-- The sum frequency should be easily filtered out by an RC filter
-- external to the FPGA.
 
hdyne_base_squarewave : dds_squarewave
generic map(
ACC_BITS => HDYNE_DDS_BITS
)
port map(
sys_rst_n => sys_rst_n,
sys_clk => sys_clk,
sys_clk_en => sys_clk_en,
 
-- Frequency setting
freq_i => INCREMENT_1000KHZ,
 
-- Output
pulse_o => open,
squarewave_o => hdyne_base_square
);
 
hdyne_in_squarewave : dds_squarewave_phase_load
generic map(
ACC_BITS => HDYNE_DDS_BITS
)
port map(
sys_rst_n => sys_rst_n,
sys_clk => sys_clk,
sys_clk_en => sys_clk_en,
 
-- Frequency setting
freq_i => hdyne_in_freq,
 
-- Synchronous load
phase_i => ((HDYNE_DDS_BITS-2)=>'1', others=>'0'), -- Set to 1/4 phase point (sinewave "zero" output)
phase_ld_i => hdyne_dds_sync,
 
-- Output
pulse_o => open,
squarewave_o => hdyne_in_square
);
 
hdyne_in_freq <= INCREMENT_1100KHZ when irig_carrier=4 else
INCREMENT_1010KHZ when irig_carrier=3 else
INCREMENT_1001KHZ when irig_carrier=2 else
INCREMENT_1000100HZ;
hdyne_out <= hdyne_base_square xor hdyne_in_square; -- A sort of digital signal multiplication
modulated_carrier <= hdyne_out when time_pulses='1' else hdyne_base_square; -- Output midpoint when pulse is low
carrier_o(0) <= '0' when irig_carrier=0 else hdyne_out;
carrier_o(1) <= time_pulses when irig_carrier=0 else
modulated_carrier;
ref_o <= '0' when irig_carrier=0 else hdyne_base_square; -- Used as a "midpoint" voltage reference
 
--------------------------
-- Setting for output filter capacitor.
-- The idea here is to ground the larger capacitor for low frequency
-- carriers, and for the highest frequency, not ground it. It will
-- be connected in series with a smaller capacitor then.
cap_gnd_o <= '0' when irig_carrier=4 else
'1';
 
-- This process is where the different BCD values are compiled into
-- a "straight binary seconds" (SBS) field.
-- The calculations are done in a multiply accumulate fashion (MAC)
-- wherein the multiply is implemented as a series of additions.
-- An SBS accumulator stores the intermediate calculation total,
-- while a state variable tracks the current stage of calculation,
-- beginning at "tens of hours" and finishing at "seconds."
-- All the adds performed here are unsigned...
sbs_proc : process(sys_clk,sys_rst_n)
begin
if (sys_rst_n='0') then
sbs_state <= RESET;
sbs <= (others=>'0');
sbs_next <= (others=>'0');
sbs_add_count <= (others=>'0');
elsif (sys_clk'event and sys_clk='1') then
if (sys_clk_en='1') then
case (sbs_state) is
when RESET =>
sbs_state <= IDLE;
when IDLE =>
if (pps='1') then
sbs_state <= TIMEKEEPING_DELAY;
end if;
-- This state waits for changes to the BCD time values to propagate,
-- so they will be stable before this process begins calculation of
-- the SBS field.
when TIMEKEEPING_DELAY =>
sbs <= (others=>'0'); -- Clear accumulator
sbs_next <= u_resize(hours_1,sbs'length);
sbs_add_count <= "1010"; -- Multiply by 10
sbs_state <= SBS_10H;
-- The following series of states performs multiply and accumulate
-- calculations to generate the SBS value.
when SBS_10H =>
if (sbs_add_count=0) then
sbs <= (others=>'0');
sbs_next <= sbs + u_resize(hours_0,sbs'length);
sbs_add_count <= "0110"; -- Multiply by 6
sbs_state <= SBS_1H;
else
sbs <= sbs + sbs_next;
sbs_add_count <= sbs_add_count-1;
end if;
when SBS_1H =>
if (sbs_add_count=0) then
sbs <= (others=>'0');
sbs_next <= sbs + u_resize(mins_1,sbs'length);
sbs_add_count <= "1010"; -- Multiply by 10
sbs_state <= SBS_10M;
else
sbs <= sbs + sbs_next;
sbs_add_count <= sbs_add_count-1;
end if;
when SBS_10M =>
if (sbs_add_count=0) then
sbs <= (others=>'0');
sbs_next <= sbs + u_resize(mins_0,sbs'length);
sbs_add_count <= "0110"; -- Multiply by 6
sbs_state <= SBS_1M;
else
sbs <= sbs + sbs_next;
sbs_add_count <= sbs_add_count-1;
end if;
when SBS_1M =>
if (sbs_add_count=0) then
sbs <= (others=>'0');
sbs_next <= sbs + u_resize(secs_1,sbs'length);
sbs_add_count <= "1010"; -- Multiply by 10
sbs_state <= SBS_10S;
else
sbs <= sbs + sbs_next;
sbs_add_count <= sbs_add_count-1;
end if;
when SBS_10S =>
if (sbs_add_count=0) then
sbs <= sbs + u_resize(secs_0,sbs'length); -- Add seconds (no more multiplication.)
sbs_state <= IDLE;
else
sbs <= sbs + sbs_next;
sbs_add_count <= sbs_add_count-1;
end if;
when others =>
null;
end case;
end if; -- sys_clk_en
end if; -- sys_clk
end process;
 
-- sr load values
p0 <= "00000" & secs_0 when (irig_rate=4) else
"0" & secs_1 & "0" & secs_0;
p1 <= "000000" & secs_1 when (irig_rate=4) else
"0" & mins_1 & "0" & mins_0;
p2 <= "00000" & mins_0 when (irig_rate=4) else
"00" & hours_1 & "0" & hours_0;
p3 <= "000000" & mins_1 when (irig_rate=4) else
days_1 & "0" & days_0;
p4 <= "00000" & hours_0 when (irig_rate=4) else
"0000000" & days_2 when (irig_rate=0 or irig_rate=1) else
tenths & "000" & days_2;
p5 <= "0000000" & hours_1 when (irig_rate=4) else
control(8 downto 4) & hundredths when (irig_rate=3) else
years_1 & '0' & years_0 when (irig_codes(2)='1') else
control(8 downto 0) when (irig_codes(0)='1') else
"000000000";
p6 <= "00000" & days_0 when (irig_rate=4) else
years_1 & '0' & years_0 when (irig_rate=3 and irig_codes(2)='1') else
control(17 downto 9) when (irig_codes(0)='1') else
"000000000";
p7 <= "00000" & days_1 when (irig_rate=4) else
control(26 downto 18) when (irig_codes(0)='1') else
"000000000";
p8 <= "0000000" & days_2 when (irig_rate=4) else
sbs(8 downto 0) when (irig_codes(1)='1') else
control(35 downto 27) when (irig_codes(0)='1') else
"000000000";
p9 <= "111110000" when (irig_rate=4) else
'0' & sbs(16 downto 9) when (irig_codes(1)='1') else
control(44 downto 36) when (irig_codes(0)='1') else
"000000000";
 
-- sr_load_bus mux
with (p_count) select
sr_load_bus <=
p0 when "0000", -- "0";
p1 when "0001", -- "1";
p2 when "0010", -- "2";
p3 when "0011", -- "3";
p4 when "0100", -- "4";
p5 when "0101", -- "5";
p6 when "0110", -- "6";
p7 when "0111", -- "7";
p8 when "1000", -- "8";
p9 when "1001", -- "9";
"000000000" when others; -- Not needed for synthesis...
 
-- Generate a clock enable for advancing the output pulse state
-- The rate of this pulse is determined by the selected IRIG rate.
-- The rate has been selected to allow output pulse duty cycle
-- to be set in increments of 1/10th the total pulse time.
--
-- Selected Rate pulse_clk rate Pulse Frequency
-- ------------- -------------- -----------------
-- 000 1 kHz 100 Hz
-- 001 (B) 1 kHz 100 Hz
-- 010 (A) 10 kHz 1 kHz
-- 011 (G) 100 kHz 10 kHz
-- 100 (NASA 36) 1 kHz 100 Hz
--
-- Generate a clock enable for advancing the output carrier state
-- The rate of this pulse is determined by the selected IRIG rate.
-- The rate has been selected to allow 10 digital samples per cycle
-- of carrier output.
--
-- Selected Carrier carrier_clk rate Carrier Frequency
-- ---------------- ---------------- -----------------
-- 000 DC Level -None-
-- 001 1 kHz 100 Hz
-- 010 10 kHz 1 kHz
-- 011 100 kHz 10 kHz
-- 100 1 MHz 100 kHz
--
 
pulse_clk_proc : process(sys_clk,sys_rst_n)
begin
if (sys_rst_n='0') then
pulse_clk_count <= (others=>'0');
elsif (sys_clk'event and sys_clk='1') then
if (sys_clk_en='1') then
if (pulse_clk_en='1') then
pulse_clk_count <= (others=>'0');
elsif (tx_1mhz_pulse='1') then
pulse_clk_count <= pulse_clk_count + 1;
end if;
end if; -- sys_clk_en
end if; -- sys_clk
end process;
pulse_clk_en <= '1' when (tx_1mhz_pulse='1' and (irig_rate=0 or irig_rate=1 or irig_rate=4) and pulse_clk_count=999) else
'1' when (tx_1mhz_pulse='1' and irig_rate=2 and pulse_clk_count=99) else
'1' when (tx_1mhz_pulse='1' and irig_rate=3 and pulse_clk_count=9) else
'0';
 
-- This is the IRIG timecode generating state machine.
-- It handles updates to the timekeeping registers, based on the "pps" signal,
-- and creates the output pulse stream.
fsm_proc: process(sys_clk, sys_rst_n)
begin
if (sys_rst_n='0') then
fsm_state <= RESET; -- asynchronous reset
leap_year <= '0';
micro_bcd_0 <= to_unsigned(0,4);
micro_bcd_1 <= to_unsigned(9,4);
micro_bcd_2 <= to_unsigned(9,4);
micro_bcd_3 <= to_unsigned(9,4);
micro_bcd_4 <= to_unsigned(9,4);
micro_bcd_5 <= to_unsigned(9,4);
hundredths <= (others=>'0');
tenths <= (others=>'0');
days_0 <= DEF_R_0(3 downto 0);
days_1 <= DEF_R_0(7 downto 4);
days_2 <= DEF_R_0(9 downto 8);
years_0 <= DEF_R_0(19 downto 16);
years_1 <= DEF_R_0(23 downto 20);
secs_0 <= DEF_R_1(3 downto 0);
secs_1 <= DEF_R_1(6 downto 4);
mins_0 <= DEF_R_1(11 downto 8);
mins_1 <= DEF_R_1(14 downto 12);
hours_0 <= DEF_R_1(19 downto 16);
hours_1 <= DEF_R_1(21 downto 20);
control <= DEF_R_3(12 downto 0) & DEF_R_2;
reg_rate <= DEF_R_4(2 downto 0);
reg_carrier <= DEF_R_5(2 downto 0);
reg_codes <= DEF_R_6(2 downto 0);
 
p_count <= (others=>'0');
pulse_sr <= (others=>'0');
pulse_sr_count <= (others=>'0');
pulse_sub_count <= (others=>'0');
cycle_count <= (others=>'0');
fsm_ack <= '0';
hdyne_dds_sync <= '0';
 
elsif (sys_clk'event and sys_clk='1') then
if (sys_clk_en='1') then
 
-- Default values
fsm_ack <= '0';
hdyne_dds_sync <= '0';
 
-- Use DDS to advance the six digit microsecond BCD counter.
-- This can be overridden by other statements further below.
if (tx_1mhz_pulse='1') then
if (micro_bcd_0=9) then
micro_bcd_0 <= (others=>'0');
if (micro_bcd_1=9) then
micro_bcd_1 <= (others=>'0');
if (micro_bcd_2=9) then
micro_bcd_2 <= (others=>'0');
if (micro_bcd_3=9) then
micro_bcd_3 <= (others=>'0');
if (micro_bcd_4=9) then
micro_bcd_4 <= (others=>'0');
if (micro_bcd_5=9) then
micro_bcd_5 <= (others=>'0');
else
micro_bcd_5 <= micro_bcd_5+1;
end if;
else
micro_bcd_4 <= micro_bcd_4+1;
end if;
else
micro_bcd_3 <= micro_bcd_3+1;
end if;
else
micro_bcd_2 <= micro_bcd_2+1;
end if;
else
micro_bcd_1 <= micro_bcd_1+1;
end if;
else
micro_bcd_0 <= micro_bcd_0+1;
end if;
end if;
-- Handle timekeeping for fractions of seconds, based on 1MHz clock enable.
-- These updates are overridden by pps, which resets all the time count values.
if (tx_1mhz_pulse='1') then
if ( -- Check for .01 second transition
micro_bcd_0=9 and
micro_bcd_1=9 and
micro_bcd_2=9 and
micro_bcd_3=9
) then
if (hundredths=9) then
tenths <= tenths+1;
hundredths <= (others=>'0');
if (tenths=9) then
-- No need to increment seconds here. pps does that.
tenths <= (others=>'0');
else
tenths <= tenths+1;
end if;
else
hundredths <= hundredths+1;
end if;
end if;
end if;
 
-- Handle the timekeeping items, based on pps
-- The highest priority is "set_time" which immediately resets all time
-- quantities. Next highest priority is normal timekeeping, and finally,
-- the lowest priority is register writes to time quantities.
if (set_time='1') then
micro_bcd_0 <= (others=>'0');
micro_bcd_1 <= (others=>'0');
micro_bcd_2 <= (others=>'0');
micro_bcd_3 <= (others=>'0');
micro_bcd_4 <= (others=>'0');
micro_bcd_5 <= (others=>'0');
secs_0 <= rx_secs_0;
secs_1 <= rx_secs_1;
mins_0 <= rx_mins_0;
mins_1 <= rx_mins_1;
hours_0 <= rx_hours_0;
hours_1 <= rx_hours_1;
days_0 <= rx_days_0;
days_1 <= rx_days_1;
days_2 <= rx_days_2;
years_0 <= rx_years_0;
years_1 <= rx_years_1;
elsif (pps='1') then
secs_0 <= secs_0+1;
tenths <= (others=>'0');
hundredths <= (others=>'0');
if (secs_0=9) then
secs_1 <= secs_1+1;
secs_0 <= (others=>'0');
if (secs_1=5) then
mins_0 <= mins_0+1;
secs_1 <= (others=>'0');
if (mins_0=9) then
mins_1 <= mins_1+1;
mins_0 <= (others=>'0');
if (mins_1=5) then
hours_0 <= hours_0+1;
mins_1 <= (others=>'0');
if (hours_1=2 and hours_0=3) then
days_0 <= days_0+1;
hours_1 <= (others=>'0');
hours_0 <= (others=>'0');
if (days_2>=3 and days_1>=6 and ((days_0>=5 and leap_year='0') or (days_0>=6 and leap_year='1'))) then
years_0 <= years_0+1;
days_0 <= "0001";
days_1 <= (others=>'0');
days_2 <= (others=>'0');
if (years_0=9) then
years_1 <= years_1+1;
years_0 <= (others=>'0');
if (years_1=9) then
years_1 <= (others=>'0');
end if;
end if;
elsif (days_0=9) then
days_1 <= days_1+1;
days_0 <= (others=>'0');
if (days_1=9) then
days_2 <= days_2+1;
days_1 <= (others=>'0');
end if;
end if;
elsif (hours_0=9) then
hours_1 <= hours_1+1;
hours_0 <= (others=>'0');
end if;
end if;
end if;
end if;
end if;
elsif (sel_i='1' and we_i='1') then -- Handle bus writes to registers
-- Cannot do this during pps, since this only writes one value, while
-- pps timekeeping can potentially affect all the values. Therefore,
-- generate an acknowledge signal for the register writes... so that
-- incoming bus cycles are extended when they collide with pps...
fsm_ack <= '1';
case (adr_i) is
when "0000" =>
leap_year <= dat_i(28);
days_0 <= dat_i(3 downto 0);
days_1 <= dat_i(7 downto 4);
days_2 <= dat_i(9 downto 8);
years_0 <= dat_i(19 downto 16);
years_1 <= dat_i(23 downto 20);
when "0001" =>
secs_0 <= dat_i(3 downto 0);
secs_1 <= dat_i(6 downto 4);
mins_0 <= dat_i(11 downto 8);
mins_1 <= dat_i(14 downto 12);
hours_0 <= dat_i(19 downto 16);
hours_1 <= dat_i(21 downto 20);
when "0010" =>
control(31 downto 0) <= dat_i;
when "0011" =>
control(44 downto 32) <= dat_i(12 downto 0);
when "0100" =>
reg_rate <= dat_i(2 downto 0);
when "0101" =>
reg_carrier <= dat_i(2 downto 0);
when "0110" =>
reg_codes <= dat_i(2 downto 0);
when others => null;
end case;
end if;
 
-- Handle pulse_sub_count
if (pulse_clk_en='1') then
if (sub_count_nine='1') then
pulse_sub_count <= (others=>'0');
hdyne_dds_sync <= '1'; -- Set the carrier wave phase.
else
pulse_sub_count <= pulse_sub_count + 1;
end if;
end if;
 
-- Handle state transitions
case (fsm_state) is
 
when RESET =>
fsm_state <= IDLE;
 
when IDLE =>
if (pps='1') then
fsm_state <= P0_REF;
p_count <= (others=>'0');
cycle_count <= (others=>'0');
pulse_sr_count <= (others=>'0');
end if;
 
when P0_REF =>
if (pulse_clk_en = '1' and sub_count_nine='1') then
pulse_sr_count <= pulse_sr_count+1;
pulse_sr <= sr_load_bus;
if (irig_rate=4) then
fsm_state <= P0_DOUT; -- No special reference pulse for IRIG 36-bit
else
fsm_state <= PR_REF;
end if;
end if;
 
when PR_REF =>
if (pulse_clk_en = '1' and sub_count_nine='1') then
pulse_sr_count <= pulse_sr_count+1;
fsm_state <= P0_DOUT;
end if;
 
when P0_DOUT =>
if (pulse_clk_en = '1' and sub_count_nine='1') then
if (pulse_sr_count=9) then
pulse_sr_count <= (others=>'0');
p_count <= p_count+1;
fsm_state <= P1_PLUS_REF;
else
pulse_sr <= '0' & pulse_sr(8 downto 1);
pulse_sr_count <= pulse_sr_count+1;
end if;
end if;
 
when P1_PLUS_REF =>
if (pulse_clk_en = '1' and sub_count_nine='1') then
pulse_sr_count <= pulse_sr_count+1;
pulse_sr <= sr_load_bus;
fsm_state <= P1_PLUS_DOUT;
end if;
 
when P1_PLUS_DOUT =>
if (p_count=9 and pps='1') then -- pps should not occur before p_count=9 anyway...
fsm_state <= P0_REF;
p_count <= (others=>'0');
cycle_count <= (others=>'0');
pulse_sr_count <= (others=>'0');
elsif (pulse_clk_en = '1' and sub_count_nine='1') then
if (pulse_sr_count=9) then
if (p_count=9) then
if (cycle_count=99 and irig_rate=3) then
fsm_state <= IDLE;
elsif (cycle_count=9 and irig_rate=2) then
fsm_state <= IDLE;
elsif (irig_rate=0 or irig_rate=1 or irig_rate=4) then
fsm_state <= IDLE;
else
cycle_count <= cycle_count+1;
p_count <= (others=>'0');
pulse_sr_count <= (others=>'0');
fsm_state <= P0_REF;
end if;
else
pulse_sr_count <= (others=>'0');
p_count <= p_count+1;
pulse_sr <= sr_load_bus;
fsm_state <= P1_PLUS_REF;
end if;
else
pulse_sr <= '0' & pulse_sr(8 downto 1);
pulse_sr_count <= pulse_sr_count+1;
end if;
end if;
 
--when others =>
-- fsm_state <= IDLE;
end case;
 
end if; -- sys_clk_en
end if; -- sys_clk
end process;
 
-- This pulse per second signal is for IRIG timecode generation
pps <= '1' when (tx_1mhz_pulse='1' and
(
micro_bcd_0=9 and
micro_bcd_1=9 and
micro_bcd_2=9 and
micro_bcd_3=9 and
micro_bcd_4=9 and
micro_bcd_5=9
)
) else '0';
 
-- Create signal which indicates last portion of time generator pulse.
sub_count_nine <= '1' when (pulse_sub_count=9) else '0';
 
-- Form output pulses
pulse_extent <= "0010" when (irig_rate=4 and pulse_sr_count=0 and p_count=0) else
"0101" when (irig_rate=4 and pulse_sr_count=0) else
"1000" when (pulse_sr_count=0 or fsm_state=PR_REF) else
"0101" when (pulse_sr(0)='1') else
"0010";
 
time_pulses <= '1' when (pulse_sub_count<pulse_extent) else '0';
time_dcl_o <= time_pulses;
 
-- Related to time receiver
----------------------------
 
--------------------------
-- generate pulses at 1 MHz nominal rate.
-- The term nominal is used here, because the actual rate can be "warped" up/down
-- to track an external PPS reference.
rx_mega_pulser : dds_squarewave
generic map(
ACC_BITS => RX_1MHZ_DDS_BITS
)
port map(
sys_rst_n => sys_rst_n,
sys_clk => sys_clk,
sys_clk_en => sys_clk_en,
 
-- Frequency setting
freq_i => rx_freq,
 
-- Output
pulse_o => rx_1mhz_pulse,
squarewave_o => open
);
tx_1mhz_pulse <= rx_1mhz_pulse;
 
-- Create the frequency setting
-- It is composed of two terms: the constant term for 1 MHz, and
-- the fine adjustment term, for varying slightly above or below
-- the constant value. The varying term is "pps_residusum".
rx_freq <= to_unsigned(PHASE_INCR_1MHZ,rx_freq'length) + unsigned(s_resize_se(pps_residusum,rx_freq'length));
 
 
-- An input signal conditioner for low pass filtering the
-- carrier squarewave clock signal
signal_cleanup : multi_stage_leaky_integrator
generic map(
STAGES => 2,
LEAK_FACTOR_BITS => 5, -- Inversely related to LP corner frequency. (Min=1)
HYSTERESIS_BITS => 8,
INTEGRATOR_BITS => time_cleaner_input'length
)
port map(
-- System Clock and Clock Enable
sys_rst_n => sys_rst_n,
sys_clk => sys_clk,
sys_clk_en => sys_clk_en,
 
-- Settings
input => time_cleaner_input,
hysteresis => str2u("10",8),
 
-- Integration Result
integrator => open,
 
-- Conditioned Digital Output Signal
output => time_cleaned
);
time_cleaner_input <= to_signed(64,time_cleaner_input'length) when time_cdet_i='1' else
to_signed(-64,time_cleaner_input'length);
 
-- Select the appropriate time_rx signal
time_rx <= time_cdet_i when reg_time_lp='0' else time_cleaned;
 
-- Create IRIG time receiver state report signal
with (rx_state) select
rx_state_report <=
"0011" when WAIT_ACTIVE,
"0100" when SEEK_REF_1,
"0101" when SEEK_REF_2,
"0110" when RECEIVE_PULSES,
"0111" when SETTLE_IN,
"1000" when TRACK_IRIG,
"1111" when others;
 
-- Assert outputs when in the correct state
rx_lock <= '1' when rx_state=TRACK_IRIG else '0';
lock_o <= rx_lock;
active_o <= rx_active;
-- tracking_o <= rx_irig_tracking when rx_state=TRACK_IRIG else rx_pps_tracking;
tracking_o <= rx_irig_tracking;
neverlocked_o <= rx_neverlocked;
cdet_sel_o <= '1' when rx_state=SETTLE_IN or rx_state=TRACK_IRIG else cdet_sel_l;
 
dac_clk1 : dds_clk_en_gen
generic map(
ACC_BITS => 24
)
port map(
-- Reset, System Clock and Clock Enable
sys_rst_n => sys_rst_n,
sys_clk => sys_clk,
sys_clk_en => sys_clk_en,
 
-- Frequency setting
freq_i => str2u("001A37",24),
 
-- Output
clk_en_o => cdet_dac_clk_en
);
 
dac1 : pwm_unsigned
generic map(
PERIOD => 32,
COUNT_BITS => 6
)
port map(
-- Reset, System Clock and Clock Enable
sys_rst_n => sys_rst_n,
sys_clk => sys_clk,
sys_clk_en => cdet_dac_clk_en,
 
-- Input Data
dat_i => cdet_bias,
 
-- Output Signal
dat_o => cdet_sel_l
);
 
-- Create pulse that indicates the rising edge of receive data
rx_posedge <= '1' when time_rx_r2='0' and time_rx_r1='1' else '0';
-- Create pulse that indicates the start of a new timecode pulse
-- (whether carrier modulated or DC level)
rx_pstart <= '1' when rx_posedge='1' and rx_lo_count>RX_EDGE_VAL else '0';
 
-- Select the rx_scale_factor based on carrier detect value
with (to_integer(cdet_val_l)) select
rx_scale_factor <=
to_unsigned(1,rx_scale_factor'length) when 3,
to_unsigned(10,rx_scale_factor'length) when 2,
to_unsigned(100,rx_scale_factor'length) when 1,
to_unsigned(100,rx_scale_factor'length) when others;
-- Provide cdet_val_l as an output signal
cdet_val_o <= cdet_val_l;
 
-- This is the IRIG timecode receive process
-- It handles updates to the receive time values
rx_proc: process(sys_clk, sys_rst_n)
begin
if (sys_rst_n='0') then
rx_state <= WAIT_ACTIVE;
rx_nasa36 <= '0';
rx_pulse_count <= (others=>'0');
rx_group_count <= (others=>'0');
time_rx_r1 <= '0';
time_rx_r2 <= '0';
rx_raise_count <= (others=>'0');
rx_active_count <= (others=>'0');
rx_active_wdog <= (others=>'0');
rx_hi_count <= (others=>'0');
rx_lo_count <= (others=>'0');
rx_pulse_width <= (others=>'0');
pps_r1 <= '0';
pps_r2 <= '0';
rx_sr <= (others=>'0');
rx_c_count <= (others=>'0');
pps_r_count <= to_signed(PPS_R_COUNT_SEED,pps_r_count'length);
pps_residusum <= (others=>'0');
rx_secs_0 <= (others=>'0');
rx_secs_1 <= (others=>'0');
rx_mins_0 <= (others=>'0');
rx_mins_1 <= (others=>'0');
rx_hours_0 <= (others=>'0');
rx_hours_1 <= (others=>'0');
rx_days_0 <= (others=>'0');
rx_days_1 <= (others=>'0');
rx_days_2 <= (others=>'0');
rx_years_0 <= (others=>'0');
rx_years_1 <= (others=>'0');
rx_active <= '0';
rx_pps_tracking <= '0';
rx_irig_tracking <= '0';
rx_neverlocked <= '1';
cdet_fixed <= '0';
cdet_ms_timer <= (others=>'0');
cdet_val_l <= (others=>'0');
rx_scale_count <= to_unsigned(1,rx_scale_count'length);
cdet_bias <= DEF_R_A(cdet_bias'length-1 downto 0);
reg_time_lp <= DEF_R_A(8);
set_time <= '0';
rx_stat_1 <= (others=>'0');
rx_stat_2 <= (others=>'0');
rx_stat_3 <= (others=>'0');
 
elsif (sys_clk'event and sys_clk='1') then
if (sys_clk_en='1') then
 
-- Default values
set_time <= '0'; -- pulses high to set the IRIG generator time
 
-- Handle carrier detect millisecond timer
-- Used to time the settling period when cdet_sel_o is changed
if (pulse_1ms='1') then
cdet_ms_timer <= cdet_ms_timer+1;
end if;
 
-- Service residual counter
if (rx_1mhz_pulse='1') then
pps_r_count <= pps_r_count-1;
end if;
 
-- Capture incoming carrier pulses
if (rx_1mhz_pulse='1') then
time_rx_r1 <= time_rx;
end if;
time_rx_r2 <= time_rx_r1;
 
-- Apply selected scale factor to produce 1 MHz, 100 kHz or 10 kHz rx_sample pulses
-- rx_posedge is used to synchronize the sample pulse to each new rising edge of
-- the rx line.
if (rx_1mhz_pulse='1' and rx_scale_count=1) or rx_posedge='1' then
rx_scale_count <= rx_scale_factor;
elsif (rx_1mhz_pulse='1') then
rx_scale_count <= rx_scale_count-1;
end if;
 
-- An up-counter that advances each sample time, but resets when
-- the rx line is high... rx_lo_count is to be thresholded for data detection.
-- Keeping rx_lo_count reset when the receive line is high enables
-- reception of "dc level" irig time data as well as the
-- "gated carrier pulse" data from the demodulator circuit.
if (time_rx_r2='1') then
rx_lo_count <= (others=>'0');
elsif (rx_1mhz_pulse='1' and rx_scale_count=1) then
if (rx_lo_count<255) then -- Do not let counter roll over.
rx_lo_count <= rx_lo_count+1;
end if;
end if;
-- Capture the pulse width for reporting purposes only.
if (rx_pstart='1') then
rx_pulse_width <= rx_lo_count;
end if;
 
-- Handle receive shift register
if (rx_pstart='1') then
if (rx_lo_count > RX_ZERO_VAL) then
rx_sr(rx_sr'length-2 downto 0) <= rx_sr(rx_sr'length-1 downto 1);
rx_sr(rx_sr'length-1) <= '0';
else
rx_sr(rx_sr'length-2 downto 0) <= rx_sr(rx_sr'length-1 downto 1);
rx_sr(rx_sr'length-1) <= '1';
end if;
end if;
 
-- Detect timecode activity here, by examining the rx_lo_count, and
-- adjusting the sample rate to seek a sensible input signal.
-- 1) If, at rx_posedge, rx_lo_count>RX_TOO_SHORT even once select a
-- lower sample rate.
-- 2) If, at rx_posedge, rx_lo_count<RX_EDGE_COUNT 10 times in a row,
-- select a higher sample rate.
-- 3) Require a duty cycle between 10% and 80%
-- 4) Once locked, require rx_posedge activity during an N sample
-- interval.
-- If, at rx_pstart, all the above conditions are met ten
-- times in a row, then the input signal and sample rate are
-- considered correct, and an input "active" condition is declared.
-- The "active" condition is rescinded if any of the conditions
-- are not met, at any time.
if (rx_1mhz_pulse='1' and rx_scale_count=1 and time_rx_r2='1') then
if (rx_hi_count<((2**rx_hi_count'length)-1)) then -- Do not let counter roll over.
rx_hi_count <= rx_hi_count+1;
end if;
end if;
-- Count number of times all conditions are met
if (rx_pstart='1') then
rx_hi_count <= (others=>'0');
if (rx_active='0' and rx_hi_count>9 and rx_hi_count<81) then
rx_active_count <= rx_active_count+1;
rx_raise_count <= (others=>'0');
if (rx_active_count=9) then
rx_active <= '1';
if (cdet_fixed='0' and cdet_val_l=0) then
cdet_val_l <= cdet_val_l+1;
end if;
end if;
end if;
end if;
-- Handle sample rate adjustments automatically, until a successful
-- reception of all pulse groups is achieved. Thereafter, only lose
-- lock due to watchdog.
if (rx_posedge='1' and not (rx_state=SETTLE_IN or rx_state=TRACK_IRIG)) then
if (rx_lo_count>RX_TOO_SHORT) then
rx_active_count <= (others=>'0');
rx_active <= '0';
rx_stat_1 <= rx_stat_1+1;
rx_pulse_width <= to_unsigned(16#AB#,rx_pulse_width'length);
if (cdet_fixed='0' and cdet_val_l>0) then
cdet_val_l <= cdet_val_l-1; -- Lower the sample rate
end if;
elsif (rx_lo_count<3) then
rx_raise_count <= rx_raise_count+1;
rx_active_count <= (others=>'0');
rx_active <= '0';
rx_stat_2 <= rx_stat_2+1;
rx_pulse_width <= to_unsigned(16#AA#,rx_pulse_width'length);
if (rx_raise_count=9) then
rx_raise_count <= (others=>'0');
if (cdet_fixed='0' and cdet_val_l<3) then
cdet_val_l <= cdet_val_l+1; -- Raise the sample rate
end if;
end if;
end if;
end if;
-- Handle activity watchdog
if (rx_posedge='1') then
rx_active_wdog <= (others=>'0'); -- reset activity watchdog
end if;
if (rx_1mhz_pulse='1' and rx_scale_count=1) then
rx_active_wdog <= rx_active_wdog+1;
if (rx_active='1' and rx_active_wdog=200) then
cdet_val_l <= (others=>'0');
rx_nasa36 <= '0';
rx_active_count <= (others=>'0');
rx_active <= '0';
rx_stat_3 <= rx_stat_3+1;
end if;
end if;
 
-- Handle state transitions
case (rx_state) is
 
when WAIT_ACTIVE =>
if (rx_active='1') then
rx_state <= SEEK_REF_1;
end if;
 
when SEEK_REF_1 =>
if (rx_pstart='1') then
if (rx_sr="0111110000") then
rx_pulse_count <= to_unsigned(0,rx_pulse_count'length);
rx_group_count <= to_unsigned(0,rx_group_count'length);
rx_state <= RECEIVE_PULSES;
rx_nasa36 <= '1';
elsif (rx_lo_count<=RX_ONE_VAL) then
rx_state <= SEEK_REF_2;
end if;
end if;
 
when SEEK_REF_2 =>
if (rx_pstart='1') then
if (rx_lo_count<=RX_ONE_VAL) then
rx_pulse_count <= to_unsigned(0,rx_pulse_count'length);
rx_group_count <= to_unsigned(0,rx_group_count'length);
rx_state <= RECEIVE_PULSES;
else
rx_state <= SEEK_REF_1;
end if;
end if;
 
when RECEIVE_PULSES =>
if (rx_pstart='1') then
-- Service the pulse, group and frame counters
-- Must obtain "group lock" before using received data
rx_pulse_count <= rx_pulse_count+1; -- default action
if (rx_pulse_count=9) then
rx_pulse_count <= (others=>'0');
rx_group_count <= rx_group_count+1; -- Default is to advance the count.
if (rx_group_count=9) then
rx_group_count <= (others=>'0');
cdet_ms_timer <= (others=>'0');
rx_state <= SETTLE_IN; -- At the end of the full reading, move on
rx_neverlocked <= '0';
set_time <= '1';
end if;
-- Initialize the time fields as they are received...
case (to_integer(rx_group_count)) is
when 0 =>
if (rx_sr(4 downto 1)<9) then
rx_secs_0 <= rx_sr(4 downto 1)+1;
else
rx_state <= SEEK_REF_1; -- Adding one during rollover case is too hard. Just try again later.
end if;
if (rx_nasa36='0') then
rx_secs_1 <= rx_sr(8 downto 6);
end if;
when 1 =>
if (rx_nasa36='0') then
rx_mins_0 <= rx_sr(3 downto 0);
rx_mins_1 <= rx_sr(7 downto 5);
else
rx_secs_1 <= rx_sr(2 downto 0);
end if;
when 2 =>
if (rx_nasa36='0') then
rx_hours_0 <= rx_sr(3 downto 0);
rx_hours_1 <= rx_sr(6 downto 5);
else
rx_mins_0 <= rx_sr(3 downto 0);
end if;
when 3 =>
if (rx_nasa36='0') then
rx_days_0 <= rx_sr(3 downto 0);
rx_days_1 <= rx_sr(8 downto 5);
else
rx_mins_1 <= rx_sr(2 downto 0);
end if;
when 4 =>
if (rx_nasa36='0') then
rx_days_2 <= rx_sr(1 downto 0);
else
rx_hours_0 <= rx_sr(3 downto 0);
end if;
when 5 =>
if (cdet_val_l/=3) then
rx_years_0 <= rx_sr(3 downto 0);
rx_years_1 <= rx_sr(8 downto 5);
end if;
if (rx_nasa36='1') then
rx_hours_1 <= rx_sr(1 downto 0);
end if;
when 6 =>
if (cdet_val_l=3) then
rx_years_0 <= rx_sr(3 downto 0);
rx_years_1 <= rx_sr(8 downto 5);
end if;
if (rx_nasa36='1') then
rx_days_0 <= rx_sr(3 downto 0);
end if;
when 7 =>
if (rx_nasa36='1') then
rx_days_1 <= rx_sr(3 downto 0);
end if;
when 8 =>
if (rx_nasa36='1') then
rx_days_2 <= rx_sr(1 downto 0);
end if;
when 9 =>
null;
when others =>
null;
end case;
end if;
end if; -- if rx_pstart
 
-- Spend some time waiting for the demodulator circuit to settle.
-- There are capacitors in it which need to discharge before it works
-- following a switching event.
when SETTLE_IN =>
if (cdet_ms_timer=CDET_SETTLE_TIME) then
rx_state <= TRACK_IRIG;
case (to_integer(cdet_val_l)) is
when 3 =>
rx_c_count <= to_unsigned(RX_C_SEED_G,rx_c_count'length);
when 2 =>
rx_c_count <= to_unsigned(RX_C_SEED_A,rx_c_count'length);
when 1 =>
rx_c_count <= to_unsigned(RX_C_SEED_B,rx_c_count'length);
when others =>
rx_c_count <= to_unsigned(RX_C_SEED_B,rx_c_count'length);
end case;
end if;
 
when TRACK_IRIG => -- Tracking on received IRIG time signal based PPS
-- Service carrier pulse countdown.
-- Counter is reloaded by statements below.
if (rx_posedge='1') then
if (rx_c_count=1) then
case (to_integer(cdet_val_l)) is
when 3 =>
rx_c_count <= to_unsigned(RX_C_SEED_G,rx_c_count'length);
when 2 =>
rx_c_count <= to_unsigned(RX_C_SEED_A,rx_c_count'length);
when 1 =>
rx_c_count <= to_unsigned(RX_C_SEED_B,rx_c_count'length);
when others =>
rx_c_count <= to_unsigned(RX_C_SEED_B,rx_c_count'length);
end case;
-- Service time tracking counters
pps_r_count <= to_signed(PPS_R_COUNT_SEED,pps_r_count'length);
if (abs(pps_r_count) > PPS_TRACK_LIMIT) then
rx_irig_tracking <= '0';
else
pps_residusum <= pps_residusum + s_resize_se(s_resize_l(pps_r_count,pps_r_count'length-1),pps_residusum'length); -- divide by two
rx_irig_tracking <= '1';
end if;
else
rx_c_count <= rx_c_count-1;
end if;
end if;
 
--when others =>
-- fsm_state <= IDLE;
end case;
 
-- Restart acquisition if there is no incoming activity
-- It is not helpful to increment the stats counter for this...
if (rx_state/=WAIT_ACTIVE and rx_active='0') then
pps_r_count <= to_signed(PPS_R_COUNT_SEED,pps_r_count'length);
rx_irig_tracking <= '0';
cdet_ms_timer <= (others=>'0');
cdet_val_l <= (others=>'0');
rx_state <= WAIT_ACTIVE;
end if;
 
-- Metastability mitigation chain.
-- Also used to detect rising edge of pps_i
pps_r1 <= pps_i;
pps_r2 <= pps_r1;
 
-- When not tracking IRIG input, track PPS input if present
if (rx_state/=TRACK_IRIG and pps_r2='0' and pps_r1='1') then
if (abs(pps_r_count) > PPS_TRACK_LIMIT) then
rx_pps_tracking <= '0';
else
pps_r_count <= to_signed(PPS_R_COUNT_SEED,pps_r_count'length);
pps_residusum <= pps_residusum + s_resize_se(s_resize_l(pps_r_count,pps_r_count'length-1),pps_residusum'length); -- divide by two
rx_pps_tracking <= '1';
end if;
end if;
 
-- Reset stats registers
if (sel_i='1' and we_i='1' and ack_l='1') then -- Handle bus writes to registers
if (adr_i=7) then
cdet_fixed <= dat_i(10);
if (cdet_fixed='1') then
rx_nasa36 <= dat_i(9);
cdet_val_l <= dat_i(cdet_val_l'length-1 downto 0);
end if;
end if;
if (adr_i=8) then
rx_stat_1 <= (others=>'0');
rx_stat_2 <= (others=>'0');
rx_stat_3 <= (others=>'0');
end if;
if (adr_i=10) then
cdet_bias <= dat_i(cdet_bias'length-1 downto 0);
reg_time_lp <= dat_i(8);
end if;
end if;
 
end if; -- sys_clk_en
end if; -- sys_clk
end process;
 
-- A pulse that occurs once every millisecond, based on transmit signals.
pulse_1ms <= '1' when (micro_bcd_0=9 and micro_bcd_1=9 and micro_bcd_2=9 and micro_bcd_3=9 and tx_1mhz_pulse='1') else '0';
 
end beh;
 
/irig_regenerator/trunk/rtl/pwm_pack.vhd
0,0 → 1,208
--------------------------------------------------------------------------
-- Package of Pulse Width Modulation (PWM) Components
--
--
 
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
 
package pwm_pack is
 
component pwm_simple
generic (
PERIOD : natural := 800; -- Number of sys_clk_en per PWM cycle
COUNT_BITS : natural := 10 -- LOG2(PERIOD)
);
port (
-- Reset, System Clock and Clock Enable
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Input Data
dat_i : in signed((COUNT_BITS-1) downto 0);
 
-- Output Signal
dat_o : out std_logic
);
end component;
 
component pwm_unsigned
generic (
PERIOD : natural := 800; -- Number of sys_clk_en per PWM cycle
COUNT_BITS : natural := 10 -- LOG2(PERIOD)
);
port (
-- Reset, System Clock and Clock Enable
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Input Data
dat_i : in unsigned((COUNT_BITS-1) downto 0);
 
-- Output Signal
dat_o : out std_logic
);
end component;
 
end pwm_pack;
 
package body pwm_pack is
end pwm_pack;
 
-------------------------------------------------------------------------------
-- Simple PWM unit
-------------------------------------------------------------------------------
--
-- Author: John Clayton
-- Update: Oct. 14, 2013 Added this header. Created description and code.
--
-- Description
-------------------------------------------------------------------------------
-- This is a quite simple pulse width modulator (PWM) unit. It contains a
-- counter which increments at the sys_clk_en rate. When the counter value
-- exceeds the value of the dat_i input, the output is driven high. When
-- the counter reaches its terminal value, the counter resets and the output
-- is driven low once again.
--
-- In order to use a signed input with an unsigned up-counter, the dat_i
-- input is first converted into an unsigned quantity. This is done by
-- adding 2**(COUNT_BITS-1) to the input.
--
 
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
 
entity pwm_simple is
generic (
PERIOD : natural := 800; -- Number of sys_clk_en per PWM cycle
COUNT_BITS : natural := 10 -- LOG2(PERIOD)
);
port (
-- Reset, System Clock and Clock Enable
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Input Data
dat_i : in signed((COUNT_BITS-1) downto 0);
 
-- Output Signal
dat_o : out std_logic
);
 
end pwm_simple;
 
architecture beh1 of pwm_simple is
 
-- Constants
constant adjustment : unsigned(COUNT_BITS-1 downto 0) := to_unsigned(2**(COUNT_BITS-1),COUNT_BITS);
 
-- Signals
signal count : unsigned(COUNT_BITS-1 downto 0);
signal dac_val : unsigned(COUNT_BITS-1 downto 0);
 
-----------------------------------------------------------------------------
begin -- beh1
 
dac_val <= adjustment + unsigned(dat_i);
 
process (sys_clk, sys_rst_n)
begin -- process
if sys_rst_n='0' then -- asynchronous reset (active low)
count <= to_unsigned(1,count'length);
dat_o <= '0';
elsif sys_clk'event and sys_clk='1' then -- rising clock edge
if (sys_clk_en='1') then
count <= count+1;
if (count=PERIOD) then
count <= to_unsigned(1,count'length);
end if;
if (count>=dac_val) then
dat_o <= '1';
else
dat_o <= '0';
end if;
end if;
end if;
end process;
 
end beh1;
 
-------------------------------------------------------------------------------
-- Simple PWM unit, unsigned version
-------------------------------------------------------------------------------
--
-- Author: John Clayton
-- Update: Oct. 14, 2013 Copied pwm_simple, and modified it slightly.
--
-- Description
-------------------------------------------------------------------------------
-- This is a quite simple pulse width modulator (PWM) unit. It contains a
-- counter which increments at the sys_clk_en rate. When the counter value
-- exceeds the value of the dat_i input, the output is driven high. When
-- the counter reaches its terminal value, the counter resets and the output
-- is driven low once again.
--
 
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
 
entity pwm_unsigned is
generic (
PERIOD : natural := 800; -- Number of sys_clk_en per PWM cycle
COUNT_BITS : natural := 10 -- LOG2(PERIOD)
);
port (
-- Reset, System Clock and Clock Enable
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Input Data
dat_i : in unsigned(COUNT_BITS-1 downto 0);
 
-- Output Signal
dat_o : out std_logic
);
 
end pwm_unsigned;
 
architecture beh1 of pwm_unsigned is
 
-- Constants
 
-- Signals
signal count : unsigned(COUNT_BITS-1 downto 0);
 
-----------------------------------------------------------------------------
begin -- beh1
 
process (sys_clk, sys_rst_n)
begin -- process
if sys_rst_n='0' then -- asynchronous reset (active low)
count <= to_unsigned(0,count'length);
dat_o <= '0';
elsif sys_clk'event and sys_clk='1' then -- rising clock edge
if (sys_clk_en='1') then
count <= count+1;
if (count=PERIOD-1) then
count <= to_unsigned(1,count'length);
end if;
if (count>=dat_i) then
dat_o <= '1';
else
dat_o <= '0';
end if;
end if;
end if;
end process;
 
end beh1;
 
/irig_regenerator/trunk/rtl/signal_conditioning_pack.vhd
0,0 → 1,743
--------------------------------------------------------------------------
-- Package
--
--
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
 
package signal_conditioning_pack is
 
component edge_detector
generic(
DETECT_RISING : natural;
DETECT_FALLING : natural
);
port (
-- System Clock and Clock Enable
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Input Signal
sig_i : in std_logic;
 
-- Output pulse
pulse_o : out std_logic
 
);
end component;
 
component leaky_integrator
generic(
LEAK_FACTOR_BITS : natural;
LEAK_FACTOR : natural;
INTEGRATOR_BITS : natural -- Bits in the integrating accumulator
);
port (
-- System Clock and Clock Enable
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Settings
input : in signed(INTEGRATOR_BITS-1 downto 0);
 
-- Integration Result
integrator : out signed(INTEGRATOR_BITS-1 downto 0)
 
);
end component;
 
component leaky_integrator_with_digital_output
generic(
FACTOR_BITS : natural; -- Make this less than INTEGRATOR_BITS
HYSTERESIS_BITS : natural; -- Make this less than INTEGRATOR_BITS
INTEGRATOR_BITS : natural -- Bits in the integrating accumulator
);
port (
-- System Clock and Clock Enable
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Settings
input : in signed(INTEGRATOR_BITS-1 downto 0);
leak_factor : in signed(FACTOR_BITS-1 downto 0);
hysteresis : in unsigned(HYSTERESIS_BITS-1 downto 0);
 
-- Integration Result
integrator : out signed(INTEGRATOR_BITS-1 downto 0);
 
-- Conditioned Digital Output Signal
output : out std_logic
);
end component;
 
component multi_stage_leaky_integrator
generic(
STAGES : natural;
LEAK_FACTOR_BITS : natural; -- Inversely related to LP corner frequency. (Min=1)
HYSTERESIS_BITS : natural; -- Make this less than INTEGRATOR_BITS
INTEGRATOR_BITS : natural -- Bits in each integrating accumulator
);
port (
-- System Clock and Clock Enable
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Settings
input : in signed(INTEGRATOR_BITS-1 downto 0);
hysteresis : in unsigned(HYSTERESIS_BITS-1 downto 0);
 
-- Final Stage Integration Result
integrator : out signed(INTEGRATOR_BITS-1 downto 0);
 
-- Conditioned Digital Output Signal
output : out std_logic
);
end component;
 
component integrating_pulse_stretcher
generic(
MIN_CLKS : natural; -- pulses below this many clocks are ignored
RETRIGGERABLE : natural; -- 1=restart on new pulses. 0=decay to zero before restarting
STRETCH_FACTOR : natural; -- 0=don't stretch, 1=double, 2=triple
INITIAL_OFFSET : natural; -- Value initially loaded into integrator
INTEGRATOR_BITS : natural -- Bits in the integrating accumulator
);
port (
-- System Clock and Clock Enable
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Input
pulse_i : in std_logic;
 
-- Output
pulse_o : out std_logic
 
);
end component;
 
end signal_conditioning_pack;
 
-------------------------------------------------------------------------------
-- Edge Detector
-------------------------------------------------------------------------------
--
-- Author: John Clayton
-- Date : Nov. 1, 2013 Started Coding, drawing from various other sources.
-- Created description. Simulated it and saw that it
-- works.
--
--
-- Description
-------------------------------------------------------------------------------
-- This module is a super simple edge detector, which produces is high-going
-- pulse whenever there is an edge on the input. The type of edge is
-- selectable via generic.
--
-- The sys_clk_en affects the operation by extending the length of the pulse
-- output beyond one single sys_clk cycle, as expected.
--
-- Detecting both rising and falling edges is allowed. Detecting neither is
-- also allowed, but not recommended.
--
-- The sys_rst_n input is an asynchronous reset.
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
 
library work;
use work.convert_pack.all;
 
entity edge_detector is
generic(
DETECT_RISING : natural := 1;
DETECT_FALLING : natural := 0
);
port (
-- System Clock and Clock Enable
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Input Signal
sig_i : in std_logic;
 
-- Output pulse
pulse_o : out std_logic
 
);
end edge_detector;
 
architecture beh of edge_detector is
 
-- Constants
 
-- Functions & associated types
 
-- Signal Declarations
signal sig_r1 : std_logic;
signal pulse_r : std_logic;
signal pulse_f : std_logic;
signal pulse_b : std_logic;
 
begin
 
process (sys_clk, sys_rst_n)
begin
if (sys_rst_n='0') then
sig_r1 <= '0';
elsif (sys_clk'event and sys_clk='1') then -- rising edge
if (sys_clk_en='1') then
sig_r1 <= sig_i;
end if;
end if;
end process;
 
-- The detection
pulse_r <= '1' when sig_i='1' and sig_r1='0' else '0';
pulse_f <= '1' when sig_i='0' and sig_r1='1' else '0';
pulse_b <= sig_i xor sig_r1;
 
-- The output
pulse_o <= pulse_b when DETECT_RISING/=0 and DETECT_FALLING/=0 else
pulse_r when DETECT_RISING/=0 else
pulse_f when DETECT_FALLING/=0 else
'0'; -- Haha! Don't go there!
 
end beh;
 
-------------------------------------------------------------------------------
-- Leaky Integrator
-------------------------------------------------------------------------------
--
-- Author: John Clayton
-- Date : Oct. 11, 2013 Started Coding, drawing from various other sources.
-- Created description. Simulated it and saw that it
-- works.
--
--
-- Description
-------------------------------------------------------------------------------
-- This module is a pretty simple digital "leaky integrator" designed to low
-- pass noisy signals by integrating them.
--
-- The first order differential equation is of the form:
--
-- dx/dt = -leak_factor*x + input
--
-- The input is discretized in both time and amplitude. A one bit digital
-- input can be mapped to the "input" signal by using a positive value for
-- '1' and a negative value for '0' Alternately, a signed DSP type signal
-- can be used directly.
--
-- The "leak_factor" is a feedback term, so that "DC gain" is inversely
-- proportional to the leak_factor.
--
-- Due to the presence of the feedback term, the integration's DC value, or
-- constant term, gets "leaked" to zero over time. This is nice because
-- the threshold centers around zero.
--
-- Hysteresis can be added so that the digital output transitions an amount
-- above or below zero, depending on the current value of the output.
--
-- Clear as mud? Well, it's similar to a low-pass filter, and the hysteresis
-- also helps remove noise on the input signal.
--
-- The sys_rst_n input is an asynchronous reset.
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
 
library work;
use work.convert_pack.all;
 
entity leaky_integrator is
generic(
LEAK_FACTOR_BITS : natural := 10;
LEAK_FACTOR : natural := 10;
INTEGRATOR_BITS : natural := 16 -- Bits in the integrating accumulator
);
port (
-- System Clock and Clock Enable
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Settings
input : in signed(INTEGRATOR_BITS-1 downto 0);
 
-- Integration Result
integrator : out signed(INTEGRATOR_BITS-1 downto 0)
 
);
end leaky_integrator;
 
architecture beh of leaky_integrator is
 
-- Constants
 
-- Functions & associated types
 
-- Signal Declarations
signal sum : signed(INTEGRATOR_BITS-1 downto 0);
signal delta : signed(INTEGRATOR_BITS-1 downto 0);
signal m_term : signed(INTEGRATOR_BITS+LEAK_FACTOR_BITS downto 0);
 
begin
 
-- The feedback term
m_term <= sum*to_signed(LEAK_FACTOR,LEAK_FACTOR_BITS+1);
 
-- The difference term
delta <= input - s_resize_l(m_term,delta'length);
 
-- The leaky integrator
process (sys_clk, sys_rst_n)
begin
if (sys_rst_n='0') then
sum <= to_signed(0,integrator'length);
elsif (sys_clk'event and sys_clk='1') then -- rising edge
if (sys_clk_en='1') then
sum <= sum + delta;
end if;
end if;
end process;
 
-- The outputs
integrator <= sum;
 
end beh;
 
-------------------------------------------------------------------------------
-- Leaky Integrator with conditioned Digital Output
-------------------------------------------------------------------------------
--
-- Author: John Clayton
-- Date : Oct. 11, 2013 Started Coding, drawing from various other sources.
-- Created description. Simulated it and saw that it
-- works.
--
--
-- Description
-------------------------------------------------------------------------------
-- This module is a pretty simple digital "leaky integrator" designed to clean
-- up noisy repetitive signals by integrating them, and applying thresholds
-- to the integrated result.
--
-- The first order differential equation is of the form:
--
-- dx/dt = -leak_factor*x + input
--
-- The input is discretized in both time and amplitude. A one bit digital
-- input can be mapped to the "input" signal by using a positive value for
-- '1' and a negative value for '0'
--
-- The "leak_factor" is a feedback term, so that "DC gain" is inversely
-- proportional to the leak_factor.
--
-- Due to the presence of the feedback term, the integration's DC value, or
-- constant term, gets "leaked" to zero over time. This is nice because
-- the threshold centers around zero.
--
-- Hysteresis can be added so that the digital output transitions an amount
-- above or below zero, depending on the current value of the output.
--
-- Clear as mud? Well, it's similar to a low-pass filter, and the hysteresis
-- also helps remove noise on the input signal.
--
-- The sys_rst_n input is an asynchronous reset.
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
 
library work;
use work.convert_pack.all;
 
entity leaky_integrator_with_digital_output is
generic(
FACTOR_BITS : natural := 10; -- Make this less than INTEGRATOR_BITS
HYSTERESIS_BITS : natural := 10; -- Make this less than INTEGRATOR_BITS
INTEGRATOR_BITS : natural := 16 -- Bits in the integrating accumulator
);
port (
-- System Clock and Clock Enable
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Settings
input : in signed(INTEGRATOR_BITS-1 downto 0);
leak_factor : in signed(FACTOR_BITS-1 downto 0);
hysteresis : in unsigned(HYSTERESIS_BITS-1 downto 0);
 
-- Integration Result
integrator : out signed(INTEGRATOR_BITS-1 downto 0);
 
-- Conditioned Digital Output Signal
output : out std_logic
);
end leaky_integrator_with_digital_output;
 
architecture beh of leaky_integrator_with_digital_output is
 
-- Constants
 
-- Functions & associated types
 
-- Signal Declarations
signal sum : signed(INTEGRATOR_BITS-1 downto 0);
signal delta : signed(INTEGRATOR_BITS-1 downto 0);
signal m_term : signed(INTEGRATOR_BITS+FACTOR_BITS-1 downto 0);
signal out_l : std_logic;
signal hp_term : signed(INTEGRATOR_BITS-1 downto 0);
signal hn_term : signed(INTEGRATOR_BITS-1 downto 0);
 
begin
 
-- The feedback term
m_term <= sum*leak_factor;
 
-- The difference term
delta <= input - s_resize_l(m_term,delta'length);
 
-- The leaky integrator
process (sys_clk, sys_rst_n)
begin
if (sys_rst_n='0') then
sum <= to_signed(0,integrator'length);
out_l <= '0';
elsif (sys_clk'event and sys_clk='1') then -- rising edge
if (sys_clk_en='1') then
sum <= sum + delta;
if (out_l='0') then
if (sum>hp_term) then
out_l <= '1';
end if;
else
if (sum<hn_term) then
out_l <= '0';
end if;
end if;
end if;
end if;
end process;
 
-- The hysteresis terms
hp_term <= signed(u_resize(hysteresis,INTEGRATOR_BITS));
hn_term <= not(hp_term)+1;
 
-- The outputs
integrator <= sum;
output <= out_l;
 
end beh;
 
-------------------------------------------------------------------------------
-- Multi Stage Leaky Integrator with Digital Output
-------------------------------------------------------------------------------
--
-- Author: John Clayton
-- Date : Oct. 11, 2013 Started Coding, drawing from various other sources.
-- Created description.
--
--
-- Description
-------------------------------------------------------------------------------
-- This module implements N-stages of leaky integrators. Each stage has the
-- same leak factor, which is a value fixed by generics. Use powers of two
-- to avoid inferring multipliers. The smallest value of 1 is actually a
-- pretty nice choice. The LEAK_FACTOR_BITS generic determines how small
-- the leak factor is by setting the number of bits. The integer is converted
-- as follows:
--
-- multiplicand = to_signed(LEAK_FACTOR,LEAK_FACTOR_BITS);
--
-- It seems that the smallest signed number is 2 bits wide - one for a sign
-- bit, and the other for magnitude. So, LEAK_FACTOR_BITS must be >=2.
-- Between stages, an arithmetic shift right operation is inserted, which is
-- intended to automatically scale the signal to fit the next integrator.
--
-- The hysteresis value is only applied at the final stage output, to derive
-- the conditioned digital output signal.
--
-- The "leak_factor" is a feedback term, so that "DC gain" is inversely
-- proportional to the leak_factor.
--
-- Due to the presence of the feedback term, the integration's DC value, or
-- constant term, gets "leaked" to zero over time. This is nice because
-- the threshold centers around zero.
--
-- Hysteresis can be added so that the digital output transitions an amount
-- above or below zero, depending on the current value of the output.
--
-- Clear as mud? Well, it's similar to a low-pass filter, and the hysteresis
-- also helps remove noise on the input signal.
--
-- The more stages are used, the more low-pass filtering occurs.
--
-- The sys_rst_n input is an asynchronous reset.
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
 
library work;
use work.convert_pack.all;
use work.signal_conditioning_pack.all;
 
entity multi_stage_leaky_integrator is
generic(
STAGES : natural := 2;
LEAK_FACTOR_BITS : natural := 5; -- Inversely related to LP corner frequency. (Min=1)
HYSTERESIS_BITS : natural := 8; -- Make this less than INTEGRATOR_BITS
INTEGRATOR_BITS : natural := 16 -- Bits in each integrating accumulator
);
port (
-- System Clock and Clock Enable
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Settings
input : in signed(INTEGRATOR_BITS-1 downto 0);
hysteresis : in unsigned(HYSTERESIS_BITS-1 downto 0);
 
-- Final Stage Integration Result
integrator : out signed(INTEGRATOR_BITS-1 downto 0);
 
-- Conditioned Digital Output Signal
output : out std_logic
);
end multi_stage_leaky_integrator;
 
architecture beh of multi_stage_leaky_integrator is
 
-- Constants
-- This value has been found to preserve amplitude between stages,
-- without appreciable growth or shrinkage of sum levels.
constant STAGE_ASR : natural := LEAK_FACTOR_BITS+1;
constant LEAK_FACTOR : natural := 1;
 
-- Functions & associated types
 
-- Signal Declarations
type sum_type is array (natural range STAGES-1 downto 0) of signed(INTEGRATOR_BITS-1 downto 0);
signal sum_in : sum_type;
signal sum_out : sum_type;
signal out_l : std_logic;
signal hp_term : signed(INTEGRATOR_BITS-1 downto 0);
signal hn_term : signed(INTEGRATOR_BITS-1 downto 0);
 
begin
 
 
----------------------------------------------
leaky_gen : for nvar in 0 to STAGES-1 generate
begin
lint : leaky_integrator
generic map(
LEAK_FACTOR_BITS => LEAK_FACTOR_BITS,
LEAK_FACTOR => LEAK_FACTOR,
INTEGRATOR_BITS => INTEGRATOR_BITS
)
port map(
-- System Clock and Clock Enable
sys_rst_n => sys_rst_n,
sys_clk => sys_clk,
sys_clk_en => sys_clk_en,
 
-- Settings
input => sum_in(nvar),
 
-- Integration Result
integrator => sum_out(nvar)
 
);
end generate;
 
sum_in(0) <= input;
sum_gen : for nvar in 1 to STAGES-1 generate
begin
sum_in(nvar) <= asr_function(sum_out(nvar-1),STAGE_ASR);
end generate;
 
-- The hysteresis and digital output
process (sys_clk, sys_rst_n)
begin
if (sys_rst_n='0') then
out_l <= '0';
elsif (sys_clk'event and sys_clk='1') then -- rising edge
if (sys_clk_en='1') then
if (out_l='0') then
if (sum_out(STAGES-1)>hp_term) then
out_l <= '1';
end if;
else
if (sum_out(STAGES-1)<hn_term) then
out_l <= '0';
end if;
end if;
end if;
end if;
end process;
 
-- The hysteresis terms
hp_term <= signed(u_resize(hysteresis,INTEGRATOR_BITS));
hn_term <= not(hp_term)+1;
 
-- The outputs
integrator <= sum_out(STAGES-1);
output <= out_l;
 
end beh;
 
-------------------------------------------------------------------------------
-- Integrating Pulse Stretcher
-------------------------------------------------------------------------------
--
-- Author: John Clayton
-- Date : Oct. 24, 2013 Started Coding, drawing from various other sources.
-- Created description. Simulated it and saw that it
-- works.
--
--
-- Description
-------------------------------------------------------------------------------
-- This module is a pretty simple digital pulse stretcher. A high input pulse
-- present for more than MIN_CLKS clock cycles causes the INITIAL_OFFSET value
-- to be loaded into an accumulator. The accumulator is then incremented by
-- STRETCH_FACTOR each clock cycle that the input pulse remains high, and is
-- decremented by one for each clock cycle that the input pulse is low.
--
-- The output is driven high whenever the accumulator is positive.
--
-- The output pulse width is given by:
--
-- Tpulse_o = STRETCH_FACTOR*(Tpulse_i-MIN_CLKS) + INITIAL_OFFSET
--
-- The INITIAL_OFFSET can be used to overcome the effects of minimum input
-- pulse filtering. Also, if a negative value is used for it, then the
-- output pulse is delayed by additional clock cycles beyond the MIN_CLKS
-- of delay already present. Another use for the INITIAL_OFFSET value is
-- to "bridge together" a train of pulses which might have some jitter.
-- The INITIAL_OFFSET value allows for some slop of jitter to be covered
-- by the extra decay time, so that no "gaps" appear in the output pulse.
--
-- During the decay time, when the output is high, the arrival of a new pulse
-- will cause further charging of the accumulator. There is no minimum pulse
-- width for this to occur. The MIN_CLKS only applies to starting the unit
-- from a quiescent state.
--
-- The integrator is a signed quantity, so set INTEGRATOR_BITS accordingly,
-- because the integrator can only hold positive quantities up to
-- 2^(INTEGRATOR_BITS-1)-1.
--
-- The sys_rst_n input is an asynchronous reset.
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
 
library work;
use work.convert_pack.all;
 
entity integrating_pulse_stretcher is
generic(
MIN_CLKS : natural := 5; -- pulses below this many clocks are ignored
RETRIGGERABLE : natural := 1; -- 1=restart on new pulses. 0=decay to zero before restarting
STRETCH_FACTOR : natural := 2; -- 0=no output, 1=same size, 2=double
INITIAL_OFFSET : natural := 25; -- Value initially loaded into integrator
INTEGRATOR_BITS : natural := 16 -- Bits in the integrating accumulator
);
port (
-- System Clock and Clock Enable
sys_rst_n : in std_logic;
sys_clk : in std_logic;
sys_clk_en : in std_logic;
 
-- Input
pulse_i : in std_logic;
 
-- Output
pulse_o : out std_logic
 
);
end integrating_pulse_stretcher;
 
architecture beh of integrating_pulse_stretcher is
 
-- Constants
constant RUNT_BITS : natural := timer_width(MIN_CLKS);
 
-- Functions & associated types
 
-- Signal Declarations
signal runt_count : unsigned(RUNT_BITS-1 downto 0);
signal sum : signed(INTEGRATOR_BITS-1 downto 0);
signal pulse_i_r1 : std_logic;
signal pulse_l : std_logic;
 
begin
 
-- The integrator
process (sys_clk, sys_rst_n)
begin
if (sys_rst_n='0') then
runt_count <= to_unsigned(0,runt_count'length);
sum <= to_signed(0,sum'length);
pulse_i_r1 <= '0';
elsif (sys_clk'event and sys_clk='1') then -- rising edge
if (sys_clk_en='1') then
-- Detect rising edge of input pulse
pulse_i_r1 <= pulse_i;
-- When the pulse_i input is consecutively high, decrement the runt
-- counter until it is zero.
if (runt_count>0) then
if (pulse_i='1') then
runt_count <= runt_count-1;
else
runt_count <= to_unsigned(MIN_CLKS,runt_count'length);
end if;
end if;
-- Trigger on rising edge of pulse_i
if (pulse_i='1' and pulse_i_r1='0') then
if (pulse_l='0' or (RETRIGGERABLE=1 and pulse_l='1')) then
runt_count <= to_unsigned(MIN_CLKS,runt_count'length);
end if;
end if;
-- Load initial offset into accumulator on final runt countdown
if (runt_count=1) then
sum <= to_signed(INITIAL_OFFSET,sum'length);
end if;
-- Either accumulate or decay, depending on input state
if (pulse_i='1' and runt_count=0) then
sum <= sum+STRETCH_FACTOR;
elsif (pulse_i='0' and pulse_l='1') then
sum <= sum-1;
end if;
end if;
end if;
end process;
 
-- The output
pulse_l <= '1' when (sum>0) else '0';
pulse_o <= pulse_l;
 
end beh;
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.