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; |
|