library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; -- Version X10, prototype. Command Memory 512 Bytes. entity main is port ( RCK, -- Reference Clock RP, -- Radio Frequency Power Detected SDO -- Serial Data Out, from ADC : in std_logic; OND, -- On Device, keeps power turned on. ENL, -- Enable Lamp, enables boost regulator. ONL, -- On Lamp, closes the lamp's mosfet switch. CONV, -- Convert, initiates ADC conversion SDI, -- Serial Data In, for ADC channel configuration SCK, -- Serial Clock, for ADC communication XEN, -- Transmit Enable, for data transmission TP1, -- Test Point One, available on the programming extension. TP2, -- Test Point Two, available on the programming extension. TP3 -- Test Point Three, a dummy output for code retention. : out std_logic; xdac -- Transmit DAC Output, to set data transmit frequency : out std_logic_vector(4 downto 0)); -- Configuration and Calibration Constants. constant device_id : integer := 1; -- The identifier for this device. constant fck_divisor : integer := 17; -- Divisor to give 5 MHz on TCK. constant frequency_low : integer := 13; -- Low frequency code for data transmission. constant sample_rate : integer := 512; -- Data sampling rate. -- Stable Calibration Constants constant frequency_step : integer := 3; -- High minus low frequency code -- Operation Codes for the Command Processor constant stop_stimulus : integer := 0; constant start_stimulus : integer := 1; constant configure_sensor : integer := 2; constant set_brightness : integer := 3; constant set_pulse_len_hi : integer := 4; constant set_pulse_len_lo : integer := 5; constant set_interval_len_hi : integer := 6; constant set_interval_len_lo : integer := 7; constant set_stimulus_len_hi : integer := 8; constant set_stimulus_len_lo : integer := 9; constant enable_randomizer : integer := 10; constant select_device : integer := 11; -- Other Constants constant select_all : integer := 15; -- The all-device select id code. constant device_id_max : integer := 15; -- Defines range of id codes. constant max_brightness : integer := 5; -- For lamp full power. end; architecture behavior of main is -- Attributes to guide the compiler. attribute syn_keep : boolean; attribute nomerge : string; -- Power Controller signal USERSTDBY, CLRFLAG, STDBY : std_logic; -- Ring Oscillator and Transmit Clock component BUFBA is port (A : in std_logic; Z : out std_logic); end component; signal R1, R2, R3, TCK, FCK : std_logic; attribute syn_keep of R1, R2, R3 : signal is true; attribute nomerge of R1, R2, R3 : signal is ""; -- Byte Receiver signal RPS, -- Radio Frequency Power Synchronized IC, -- Initiate Command Reception TC, -- Terminate Command Reception RBI, -- Receive Command Byte Initiate RBD, -- Receive Command Byte Done CRCERR, -- Cyclic Redundancy Checksum Error BYTERR, -- Byte Error BYTS, -- Command Byte Strobe BITS -- Command Bit Strobe : boolean := false; signal cmd : std_logic_vector(7 downto 0); -- Command Byte -- Command Memory constant addr_max : integer := 511; signal cm_addr : std_logic_vector(8 downto 0); -- Command Memory Address signal cm_out : std_logic_vector(7 downto 0); -- Command Memory Data Out signal BYTSEL, -- Command Memory Select CMWR -- Command Memory Write : std_logic; -- Control Registers signal stimulus_len, -- Number of pulses in a stimulus, 0 for infinite. interval_len, -- Number of RCK periods in each pulse interval. pulse_len -- Number of RCK periods in each pulse. : std_logic_vector(15 downto 0) := "0000000000000000"; signal brightness -- A code to set the lamp high-speed duty cycle. : std_logic_vector(2 downto 0) := std_logic_vector(to_unsigned(max_brightness)); signal sensor_config -- A code to configure the sensor. : std_logic_vector(7 downto 0) := "00000000"; -- Command Processor signal CPA -- Command Processor Active : boolean := false; -- Stimulus Generator and Brightness Controller signal PA, -- Pulse Active SS, -- Start Stimulus SRUN, -- Stimulus Running RAND, -- Enable Pulse Randomizer LONL -- Local ONL : boolean := false; -- Sensor Sampling and Transmission signal TXEN, -- Stample Transmission Enabled TXI, -- Sample Transmit Initiate TXD, -- Sample Transmit Done TXB, -- Transmit Bit SCKEN, -- Enable SCK FHI, -- Frequency hi SSDO -- Synchronized SDO : boolean := false; -- Functions and Procedures function to_std_logic (v: boolean) return std_ulogic is begin if v then return('1'); else return('0'); end if; end function; begin -- Power Cotroller Power_Controller: entity PCU port map ( USERSTDBY => USERSTDBY, CLRFLAG => CLRFLAG, STDBY => STDBY); PowerUp: process is constant PowerUpEnd : integer := 3; variable counter : integer range 0 to PowerUpEnd := 0; begin wait until (RCK = '1'); if (counter = PowerUpEnd) then counter := counter; USERSTDBY <= '1'; CLRFLAG <= '0'; else counter := counter + 1; USERSTDBY <= '0'; if (counter = PowerUpEnd-1) then CLRFLAG <= '1'; else CLRFLAG <= '0'; end if; end if; OND <= to_std_logic(SRUN or CPA or TXEN); end process; -- Synchronize Radio Power Input. Synchronize_RP: process is begin wait until (RCK = '0'); RPS <= (RP = '1'); end process; -- Initiate Command Token Detection Initiate_Command: process is constant endcount : integer := 65; variable counter : integer range 0 to endcount := 0; begin wait until (RCK = '1'); if RPS then if (counter = endcount) then counter := endcount; IC <= true; else counter := counter + 1; IC <= false; end if; else counter := 0; IC <= false; end if; end process; -- Terminate Command Token Detection Terminate_Command: process is constant endcount : integer := 328; variable counter : integer range 0 to endcount := 0; begin wait until (RCK = '1'); if not RPS then if (counter = endcount) then counter := endcount; TC <= true; else counter := counter + 1; TC <= false; end if; else counter := 0; TC <= false; end if; end process; -- Byte Receiver Byte_Receiver: process is variable state, next_state : integer range 0 to 63 := 0; variable no_stop_bit : boolean := false; begin wait until (RCK = '1'); -- Idle state, waiting for Receive Byte Initiate. if (state = 0) then if RBI and (not RPS) then next_state := 1; else next_state := 0; end if; end if; -- Wait for a start bit. If we wait long enough, we will see the -- termination signal, in which case we abort and wait for not RPI. -- We clear no stop bit variable, which clears the global BYTERR -- signal. if (state = 1) then if TC then next_state := 63; else if RPS then next_state := 2; else next_state := 1; end if; end if; no_stop_bit := false; end if; BYTERR <= no_stop_bit; -- Once we have a start bit, we proceed through the eight bits of -- a command byte, each bit taking four states. The first bit occurs -- at state 7 and the stop bit at state 39. if (state >= 2) and (state <= 38) then next_state := state + 1; end if; -- If the stop bit is missing, we go to our byte error state. The stop -- bit should be a zero, so RBS should at this point be false. if (state = 39) then if not RPS then next_state := 63; else next_state := 62; end if; end if; -- Here we deal with unused states by directing them towards the byte -- error state. if (state > 39) and (state < 62) then next_state := 62; end if; -- In the byte error state, we set the no stop bit flag, which asserts the -- global BYTERR signal. We will not reset this flag until the Byte Receiver -- starts a new byte reception. This flag tells the Command Processor to ignore -- the entire command. We wait in the byte error state until RBI is unasserted. -- Because we do not assert RBD, the un-assertion of RBI will occur only when -- the Command Receiver encounters a Terminate Command signal. if (state = 62) then if not RBI then next_state := 0; else next_state := 62; end if; no_stop_bit := true; end if; -- In the end state, we assert Receive Byte Done and we wait for the command -- processor to un-assert Receive Byte Initiate. When we see not RBI, we return -- to the idle state. We stop asserting RBD, or refrain from asserting it, when -- we have Terminate Command. if (state = 63) then if not RBI then next_state := 0; else next_state := 63; end if; end if; RBD <= (state = 63) and (not TC); -- The eight bits of the command are set every four states during -- the command reception. for i in 0 to 7 loop if (state = 35 - i * 4) then if RPS then cmd(i) <= '1'; else cmd(i) <= '0'; end if; else cmd(i) <= cmd(i); end if; end loop; -- We assert Command Bit Strobe one RCK period before the best moment -- to sample each bit value. if (state = 34) or (state = 30) or (state = 26) or (state = 22) or (state = 18) or (state = 14) or (state = 10) or (state = 6) then BITS <= true; else BITS <= false; end if; -- The Byte Strobe signal indicates that we have a start bit and is -- useful as a test point trigger. It provides a pulse of two RCK -- periods. BYTS <= (state = 2) or (state = 3); -- Assert the new state. state := next_state; end process; -- Command Error Check Error_Check : process is variable crc, next_crc : std_logic_vector(15 downto 0); begin wait until (RCK = '1'); if IC then -- When a new command transmission starts, we preload the cyclic redundancy -- check register to all ones. crc := "1111111111111111"; else -- We use Command Bit Strobe to clock each command bit into the CRC. -- The transmitter calculates the checksum with zeros in the last -- sixteen bits, reverses the order of these checksum bits, and sends -- them as the last two bytes of the actual transmission, instead of the -- zeros it used when it calculated its own checksum. These last sixteen -- bits, thus obtained, will reset the receiver CRC to zero, provided there -- has been no corruption of the data on the way. if BITS then for i in 0 to 9 loop next_crc(i) := crc(i+1); end loop; next_crc(10) := crc(11) xor crc(0); next_crc(11) := crc(12); next_crc(12) := crc(13) xor crc(0); next_crc(13) := crc(14) xor crc(0); next_crc(14) := crc(15); next_crc(15) := to_std_logic(RPS) xor crc(0); crc := next_crc; end if; end if; -- The CRCERR flag tells us when the CRC is not zero. It will be zero when it -- has been reset by the two bytes of a correct checksum. CRCERR <= (crc /= "0000000000000000"); end process; -- Command Memory Command_Memory : entity RAM_512Byte port map ( Clock => not RCK, ClockEn => '1', Reset => '0', WE => CMWR, Address => cm_addr, Data => cmd, Q => cm_out); -- Command Processor Command_Processor: process is -- General-purpose state names for the Command Processor constant idle_s : integer := 0; constant receive_cmd_s : integer := 1; constant store_cmd_s : integer := 2; constant inc_addr_s : integer := 3; constant check_cmd_s : integer := 4; constant opcode_s : integer := 5; constant dec_count_s : integer := 6; constant check_count_s : integer := 7; -- Opcode-specific state names for the Command Processor constant k : integer := 10; constant stop_stimulus_s : integer := stop_stimulus + k; constant start_stimulus_s : integer := start_stimulus + k; constant configure_sensor_s : integer := configure_sensor + k; constant set_brightness_s : integer := set_brightness + k; constant set_pulse_len_hi_s : integer := set_pulse_len_hi + k; constant set_pulse_len_lo_s : integer := set_pulse_len_lo + k; constant set_interval_len_hi_s : integer := set_interval_len_hi + k; constant set_interval_len_lo_s : integer := set_interval_len_lo + k; constant set_stimulus_len_hi_s : integer := set_stimulus_len_hi + k; constant set_stimulus_len_lo_s : integer := set_stimulus_len_lo + k; constant enable_randomizer_s : integer := enable_randomizer + k; constant select_device_s : integer := select_device + k; -- Variables for the Command Processor variable opcode : integer range 0 to 255 := 0; variable select_id : integer range 0 to device_id_max := 0; variable state, next_state : integer range 0 to 31 := 0; variable count, addr : integer range 0 to addr_max := 0; variable randomizer_enabled : boolean := false; begin wait until (RCK = '1'); -- Idle State. if (state = idle_s) then if IC then next_state := receive_cmd_s; else next_state := idle_s; end if; addr := 0; count := 0; end if; -- Receive a command byte. We assert RBI and wait for RBD. If we see -- terminate command (TC), we look at the number of command bytes we have -- received so far. If we have less than three, we have only the checksum, -- so we go back to the idle state. If we have three or more, we move on. -- Note that the Byte Receiver aborts on TC also. if (state = receive_cmd_s) then if TC then if (addr <= 2) then next_state := idle_s; else next_state := check_cmd_s; end if; else if RBD then next_state := store_cmd_s; else next_state := receive_cmd_s; end if; end if; end if; RBI <= (state = receive_cmd_s); -- Store the new command byte in the command memory. We assert CMWR. if (state = store_cmd_s) then if not RBD then next_state := inc_addr_s; else next_state := store_cmd_s; end if; end if; CMWR <= to_std_logic(state = store_cmd_s); -- Increment the command address. If we have run out of space in the -- Command Memory, we abort our attempt to process the command, and wait -- for the next command. if (state = inc_addr_s) then addr := addr + 1; count := count + 1; if (addr = addr_max) then next_state := idle_s; else next_state := receive_cmd_s; end if; end if; -- There are two possible sources of error: a failure in the cyclic redundancy -- check (CRCERR) or an error in the structure of a command byte (BYTERR). If either -- is asserted, we go back to idle. Otherwise, we proceed to execution of the commands -- that are now stored in memory. We set the command memory address to zero in -- anticipation of reading out the first command byte. But before we clear the -- memory address, we save the number of data bytes in count so we will know -- how many bytes to read out and execute. By construction, the count must be at -- least 3 at this stage. if (state = check_cmd_s) then if CRCERR or BYTERR then next_state := idle_s; else next_state := opcode_s; end if; addr := 0; end if; -- We read a byte from the command memory and interpret it. We increment the -- command memory address so as to retrieve the next byte, which may be a -- parameter. if (state = opcode_s) then case to_integer(unsigned(cm_out)) is when stop_stimulus => next_state := stop_stimulus_s; when start_stimulus => next_state := start_stimulus_s; when configure_sensor => next_state := configure_sensor_s; when set_brightness => next_state := set_brightness_s; when set_pulse_len_hi => next_state := set_pulse_len_hi_s; when set_pulse_len_lo => next_state := set_pulse_len_lo_s; when set_interval_len_hi => next_state := set_interval_len_hi_s; when set_interval_len_lo => next_state := set_interval_len_lo_s; when set_stimulus_len_hi => next_state := set_stimulus_len_hi_s; when set_stimulus_len_lo => next_state := set_stimulus_len_lo_s; when enable_randomizer => next_state := enable_randomizer_s; when select_device => next_state := select_device_s; when others => next_state := check_count_s; end case; addr := addr + 1; count := count - 1; end if; -- This state causes all settings to be cleared to zero. if (state = stop_stimulus_s) then next_state := check_count_s; end if; -- We start a new stimulus with the SS signal. if (state = start_stimulus_s) then next_state := check_count_s; end if; -- The Start Stimulus signal we assert when we are in the start stimulus -- state or the stop stimulus state. In the former case, our intention -- is to apply the new stimulus settings and start flashing the lamp. In the -- latter case, our intention is to abort any existing stimulus. SS <= (state = start_stimulus_s) or (state = stop_stimulus_s); -- We set the select identifier, which we then compare to this device's id. -- If the device select number is not select_all and is not device_id, we -- abort command execution and await the next string of commands. if (state = select_device_s) then select_id := to_integer(unsigned(cm_out)); if (select_id = select_all) or (select_id = device_id) then next_state := dec_count_s; else next_state := idle_s; end if; end if; -- We set the sensor configuration register. if (state = configure_sensor_s) then next_state := dec_count_s; end if; -- Logic to control the sensor configuration register. if state = configure_sensor_s then for i in 0 to 7 loop sensor_config(i) <= cm_out(i); end loop; else sensor_config <= sensor_config; end if; TXEN <= (sensor_config(0) = '1'); -- We set brightness register, which controls the lamp's high-frequency -- duty cycle. if (state = set_brightness_s) then next_state := dec_count_s; end if; -- Logic to control the brightness register. We clear the brightness -- to all ones so the default setting is 100% duty cycle, or full power. if (state = stop_stimulus_s) then brightness <= std_logic_vector(to_unsigned(max_brightness)); else if (state = set_brightness_s) then for i in 0 to 2 loop brightness(i) <= cm_out(i); end loop; else brightness <= brightness; end if; end if; -- We turn on or off the pulse randomizer, based upon the parameter value. if (state = enable_randomizer_s) then next_state := dec_count_s; end if; if (state = stop_stimulus_s) then randomizer_enabled := false; else if (state = enable_randomizer_s) then randomizer_enabled := (cm_out(0) = '1'); end if; end if; RAND <= randomizer_enabled; -- Set the HI byte of the pulse length. if (state = set_pulse_len_hi_s) then next_state := dec_count_s; end if; -- Set the LO byte of the pulse length. if (state = set_pulse_len_lo_s) then next_state := dec_count_s; end if; -- Logic to control the pulse length register. if state = stop_stimulus_s then pulse_len <= "0000000000000000"; else for i in 0 to 7 loop if (state = set_pulse_len_hi_s) then pulse_len(i+8) <= cm_out(i); else pulse_len(i+8) <= pulse_len(i+8); end if; if (state = set_pulse_len_lo_s) then pulse_len(i) <= cm_out(i); else pulse_len(i) <= pulse_len(i); end if; end loop; end if; -- Set the HI byte of the interval length. if (state = set_interval_len_hi_s) then next_state := dec_count_s; end if; -- Set the LO byte of the interval length. if (state = set_interval_len_lo_s) then next_state := dec_count_s; end if; -- Logic to control the interval length register. if state = stop_stimulus_s then interval_len <= "0000000000000000"; else for i in 0 to 7 loop if (state = set_interval_len_hi_s) then interval_len(i+8) <= cm_out(i); else interval_len(i+8) <= interval_len(i+8); end if; if (state = set_interval_len_lo_s) then interval_len(i) <= cm_out(i); else interval_len(i) <= interval_len(i); end if; end loop; end if; -- Set the HI byte of the stimulus length. if (state = set_stimulus_len_hi_s) then next_state := dec_count_s; end if; -- Set the LO byte of the stimulus length. if (state = set_stimulus_len_lo_s) then next_state := dec_count_s; end if; -- Logic to control the stimulus length register. if state = stop_stimulus_s then stimulus_len <= "0000000000000000"; else for i in 0 to 7 loop if (state = set_stimulus_len_hi_s) then stimulus_len(i+8) <= cm_out(i); else stimulus_len(i+8) <= stimulus_len(i+8); end if; if (state = set_stimulus_len_lo_s) then stimulus_len(i) <= cm_out(i); else stimulus_len(i) <= stimulus_len(i); end if; end loop; end if; -- We increment the command memory address and decrement the command counter -- in preparation for reading the next op-code. if (state = dec_count_s) then addr := addr + 1; count := count - 1; next_state := check_count_s; end if; -- We check the command count to see if there are any more codes to be read -- from the command memory. The last two are always the checksum, so if they -- are all that is left, we will return to the idle state. if (state = check_count_s) then if (count <= 2) then next_state := idle_s; else next_state := opcode_s; end if; end if; -- Advance the state variable. state := next_state; -- Command Processor Active is true whenever the state is not idle. CPA <= (state /= idle_s); -- The Command Memory Address is always equal to the Command Processor's -- addr variable. cm_addr <= std_logic_vector(to_unsigned(addr)); end process; -- Stimulus Generator Stimulus_Generator : process is variable state, next_state : integer range 0 to 5; variable sl, il, pl : integer range 0 to 65535 := 0; begin wait until (RCK = '1'); if SS then -- The Start Stimulus pulse always drives us to the starting -- state, regardless of where we are in a stimulus. next_state := 1; else -- The zero state transitions immediately to our waiting state. if (state = 0) then next_state := 5; end if; -- We start a stimulus. The Lamp is off. We copy the stimulus, -- interval, and pulse lengths into local counters. This is the -- first RCK period of a the first pulse and the first interval. -- If the pulse length or interval length is zero, we are already -- done, so we go to our done state. If they are both non-zero, -- we generate pulses. if (state = 1) then sl := to_integer(unsigned(stimulus_len)); il := to_integer(unsigned(interval_len)); pl := to_integer(unsigned(pulse_len)); if (pl = 0) or (il = 0) then next_state := 5; else next_state := 2; end if; end if; -- Here the lamp is on. We are continuing an interval that started one -- RCK period ago, either in state 1 or state 4. But the pulse begins -- and ends in this state. We are decrementing the pulse and interval -- counters. When the pulse counter reaches one, we move to the next -- state. When the interval counter reaches two, we move on, even if the -- pulse is not complete. We note that the interval and pulse lengths -- could both be zero. if (state = 2) then if (pl <= 1) or (il <= 3) then next_state := 3; else pl := pl - 1; il := il - 1; next_state := 2; end if; end if; -- Here the lamp is off and we are waiting for the end of the interval. -- When the interval counter reaches 3, we decrement the stimulus counter -- and move on. The interval is one RCK period from being over. if (state = 3) then if (il <= 3) then sl := sl - 1; next_state := 4; else il := il - 1; next_state := 3; end if; end if; -- Here the lamp is off. If the stimulus counter is zero and we the -- stimulus length is not set to the infinite pulses code (all zeros), -- we go to our end state. Otherwise, we re-load the interval and pulse -- length counters and start another pulse. if (state = 4) then if (sl = 0) and (stimulus_len /= "0000000000000000") then next_state := 5; else il := to_integer(unsigned(interval_len)); pl := to_integer(unsigned(pulse_len)); if (pl = 0) or (il = 0) then next_state := 5; else next_state := 2; end if; end if; end if; -- This is our done state, in which we are waiting for Start Stimulus. -- The lamp is off. if (state = 5) then next_state := 5; end if; end if; state := next_state; -- We assert PA when we want the lamp to turn on, although its output may be -- modulated to reduce brightness. PA <= (state = 2); -- We assert SRUN to keep the logic chip powered up. SRUN <= (state > 0) and (state < 5); -- We assert ENL to turn on the boost regulator. ENL <= to_std_logic(SRUN); end process; -- Ring Oscillator. R1 <= to_std_logic((SRUN or TXI or TXD) and (R3 = '0')); del1 : BUFBA port map (R1,R2); del2 : BUFBA port map (R2,R3); -- Transmit Clock. Transmit_Clock: process is constant count_half : integer := fck_divisor; constant count_full : integer := fck_divisor * 2 - 1; constant fmck_offset : integer := 2; constant fmck_length : integer := 2; variable count : integer range 0 to count_full := 0; begin -- The R1 ring oscillator output runs at around 150 MHz. wait until (R3 = '1'); -- The dk_divisor is set at the top of the code, and should be such that -- we divide R1 down to 5 MHz. The period must lie within 195-215 ns. This -- dividor guarantees that the duty cycle of the clock is exactly 50%. if (count = count_full) then count := 0; else count := count + 1; end if; -- The Transmit Clock drives the Sample Transmit state machine. TCK <= to_std_logic(count >= count_half); -- When we generate an outgoing bit stream for frequency modulation -- in the sample transmission, we need a clock that is double the -- Transmit Clock frequency, but it does not have to be symmetric. FCK <= to_std_logic( ((count >= fmck_offset) and (count <= fmck_offset + fmck_length)) or ((count >= count_half + fmck_offset) and (count <= count_half + fmck_offset + fmck_length))); end process; -- Brightness Controller Brightness_Controller : process is variable count : integer range 0 to max_brightness - 1 := 0; begin -- This TCK is close to 5 MHz. wait until (TCK = '1'); -- We want to modulate the lamp at 1 MHz, so we have a five-step -- counter to govern the duty cycle. if (count = max_brightness - 1) then count := 0; else count := count + 1; end if; -- We have the following relationship between the brightness code -- and the duty cycle of the lamp power during a pulse. Code 0 = 0%, -- 1 = 20%, 2 = 40%, 3 = 60%, 4 = 80%, >=5 = 100%. The default value -- of the brightness code is 5, so duty cycle is by default 100%. LONL <= PA and (to_integer(unsigned(brightness)) > count); ONL <= to_std_logic(PA and (to_integer(unsigned(brightness)) > count)); end process; -- Sample Controller Sample_Controller : process is constant divisor : integer := 32768 / sample_rate; variable counter : integer range 0 to divisor - 1 := 0; begin -- Clock RCK is 32.768 kHz. wait until (RCK = '1'); -- We divide RCK by divisor to get the sampling frequency. -- Sampling is enabled when TXEN is asserted. if (counter < divisor - 1) and TXEN then counter := counter + 1; else counter := 0; end if; -- We initiate sampling and transmission with TXI. TXI <= (counter = divisor - 1); end process; -- Sample Transmitter Sample_Transmitter : process is constant num_sync_bits : integer := 11; -- Num synchronizing bits at start. constant num_id_bits : integer := 4; -- Number of ID bits. constant num_start_bits : integer := 1; -- Num zero start bits. constant num_stop_bits : integer := 2; -- For state machine termination only. constant num_data_bits : integer := 16; -- Number of ADC data bits. constant num_xmit_bits : integer := -- Number of transmission bit periods. num_sync_bits + num_start_bits + num_id_bits + num_data_bits + num_id_bits; constant st_done : integer := -- Final state of sample transmit machine. num_xmit_bits + num_stop_bits; constant first_sync_bit : integer := 1; constant first_start_bit : integer := first_sync_bit + num_sync_bits; constant first_id_bit : integer := first_start_bit + num_start_bits; constant first_data_bit : integer := first_id_bit + num_id_bits; constant first_iid_bit : integer := first_data_bit + num_data_bits; constant start_sck : integer := -- State for first SCK falling edge. first_data_bit - 1; constant end_sck : integer := -- State for last SCK falling edge. start_sck + num_data_bits - 1; constant id : std_logic_vector(3 downto 0) := std_logic_vector(to_unsigned(device_id,4)); -- The device id bits. variable state : integer range 0 to 63 := 0; -- Stample Transmit State begin wait until (TCK = '1'); -- We reset the state when TXI is false. Otherwise, we increment -- the state until it reaches st_done. At st_done, the state remains -- fixed until TXI is once more false. The state starts off at -- zero, when we first enable sample transmission. When TXI is -- asserted, the ring oscillator turns on, enabling TCK. On the first -- rising edge of TCK, the state becomes 1, and thereafter increments -- to st_done. Now TXD is true, which keeps the ring oscillator -- running while TXI becomes false. Once false, the state switches back -- to zero, TXD is unasserted, and the ring oscillator can turn off. -- With no rising edges on TCK, the state remains zero, in anticipation -- of the next sample transmission. if (not TXI) then state := 0; else if (state < st_done) then state := state + 1; else state := st_done; end if; end if; -- We synchronise the SDO input with TCK. SSDO <= (SDO = '1'); -- TXD indicates to other processes that the transmission is complete. TXD <= (state = st_done); -- TXB is the outgoing bit value for the data transmission. TXB <= ((state >= 0) and (state < first_start_bit)) or ((state = first_id_bit + 0) and (id(3) = '1')) or ((state = first_id_bit + 1) and (id(2) = '1')) or ((state = first_id_bit + 2) and (id(1) = '1')) or ((state = first_id_bit + 3) and (id(0) = '1')) or ((state >= first_data_bit) and (state < first_iid_bit) and SSDO) or ((state = first_iid_bit + 0) and (id(3) = '0')) or ((state = first_iid_bit + 1) and (id(2) = '0')) or ((state = first_iid_bit + 2) and (id(1) = '0')) or ((state = first_iid_bit + 3) and (id(0) = '0')); -- SCKEN asserts TCK onto SCK. SCKEN <= (state >= start_sck) and (state <= end_sck); -- SDI configures the ADC. SDI <= to_std_logic(state = start_sck); end process; CONV <= to_std_logic(not TXI); SCK <= to_std_logic((TCK = '0') and SCKEN); XEN <= to_std_logic(TXI and (not TXD)); -- Frequency Modulation Frequency_Modulation : process is begin -- Frequency modulation runs off the 10-MHz FCK clock. This clock is -- synchronous with TCK. It presents a rising edge over 10 ns after -- both the rising and falling edges of TCK. Thus, when we see a -- rising edge on FCK, the value of TCK and TXB are both established. wait until (FCK = '1'); -- When we are not transmitting RF power, we set the DAC output to -- zero so as to eliminate current consumption by the DAC resistors. if (not TXI) or TXD then xdac <= "00000"; FHI <= false; -- If TXB is asserted, we want the modulation frequency to go from low -- to high on the falling edge of TCK. When TXB is unasserted, we want -- the modulation frequency to go from high to low on the falling edge of -- TCK. elsif (TXB xor (TCK = '1')) then xdac <= std_logic_vector(to_unsigned(frequency_low + frequency_step,5)); FHI <= true; else xdac <= std_logic_vector(to_unsigned(frequency_low,5)); FHI <= false; end if; end process; -- Test Points. TP1 <= SDO; TP2 <= to_std_logic(FHI); -- Keeper Outputs. TP3 <= STDBY; end behavior;