--
Implantable Sensor with Lamp (A3030) Firmware, Master File library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; -- Version X10, 17-SEP-14. Command Reception and Stimulus Controller working, -- use in the A3030A for delivery ISL4C. -- Version B02, 29-SEP-14. Add data transmission but without transmission -- scatter. Re-assign command code 2 as configure_sensor, was formerly -- set_polarity. -- Version B03, 02-OCT-14. Add transmission scatter, but ring oscillator -- is erratic and we cannot get good reception. Slow down the ring oscillator with -- two extra gates. Simplifiy FCK and TCK logic. Assign high routing priority to -- FCK divider bits. -- Version B04, 24-OCT-14. Correct error by which ring oscillator turns on when -- we assert OND without enabling transmission. This error was adding to the power -- consumed during command reception. -- Version B05, 12-NOV-14. We remove one gate from the ring oscillator so as to -- give us better resolution in obtaining TCK. -- Version C01, 25-MAR-15. We stop transmitting when we receive an initiate command -- pulse, and start again when we see a terminate command pulse. -- Version C02, 10-APR-15. We turn off the lamp power boost regulator between -- so as to reduce interference with the EEG amplifier. -- Version C03, 21-APR-15. We add the acknowledgment command, whereby a byte sent -- from the Command Transmitter gets sent back on the SCT slow communications -- as an acknowledgement. -- Version D01, 18-JUN-15. Move the acknowledgement time farther from the sample -- transmit time. Transform the stimulus generator into one that provides random -- pulse generation at the specified frequency. 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 := 7; -- The identifier for this device. constant fck_divisor : integer := 12; -- Divisor to give 10 MHz on FCK, 5 MHz on TCK. constant frequency_low : integer := 12; -- 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; constant command_acknowledge : integer := 12; -- 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. constant aux_id : integer := 15; -- The channel to use for acknowledgement constant ack_code : integer := 1; -- The acknowledgement code in the acknowledge message constant ack_time : integer := 21; -- The timestamp value for acknowledgement transmission 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; attribute syn_keep of STDBY : signal is true; -- Ring Oscillator and Transmit Clock component BUFBA is port (A : in std_logic; Z : out std_logic); end component; signal R1, R2, R3, R4, R5, ROCK, TCK, FCK : std_logic; attribute syn_keep of R1, R2, R3, R4, R5 : signal is true; attribute nomerge of R1, R2, R3, R4, R5 : 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,3)); signal sensor_config -- A code to configure the sensor. : std_logic_vector(7 downto 0) := "00000000"; -- Acknowledgement Registers and Signals signal ack_key -- The byte we send back on command acknowledge. : std_logic_vector(7 downto 0) := "00000000"; signal TXA, -- Transmit Acknowledgment TXAD -- Transmit Acknowledgment Done : boolean := false; -- Command Processor signal CPA -- Command Processor Active : boolean := false; -- Stimulus Generator and Brightness Controller constant rck_per_ms : integer := 33; signal PA, -- Pulse Active STARTS, -- Start Stimulus STOPS, -- Stop Stimulus SRUN, -- Stimulus Running RAND, -- Enable Pulse Randomizer ISC, -- Initiate Stimulus Cycle ILP, -- Initiate Lamp Pulse SCRATCHBIT : boolean := false; signal STCK -- Stimulus Clock : std_logic; -- Sensor Sampling and Transmission signal TXEN, -- Sample Transmission Enabled TXI, -- Sample Transmit Initiate TXD, -- Sample Transmit Done TXB, -- Transmit Bit SCKEN, -- Enable SCK FHI, -- Frequency hi SSDO -- Synchronized SDO : boolean := false; signal scatter -- The transmission scatter value : std_logic_vector(3 downto 0) := "0000"; signal sample -- The ADC sample value : std_logic_vector(15 downto 0) := "0000000000000000"; -- 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 Signal -- We detect a long enough burst of command power to initiate -- command reception. 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 Signal -- We detect a long enough period without command power to -- terminate command reception. 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 -- We watch for a start bit and receive serial bytes when instructed -- to do so by the Command Processor with the RBI signal. 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 present, we go to our end state. If it's missing, -- we go to our byte error state. The stop bit is zero, so RPS 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 -- This process runs all the bits of a command through a sixteen-bit linear shift -- register, with local name "crc" for "cyclic redundancy check". We preset crc -- to all ones. The final sixteen bits of every command are chosen so that they -- reset the crc register to all zeros. If crc is not zero at the end of a command, -- there was some error during reception. We use the Bit Strobe (BITS) signal to -- clock crc, because BITS is asserted only when a command data bit is receive, -- not when we receive a start or stop bit. 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 -- This process detects Inititiate Command (IC) and activates the Byte Receiver. It stores -- command bytes in the Command Memory until it detects Terminate Command (TC). If the -- Error Check reports no error, the Command Processor reads the commands out of command -- memory one at a time and executes them. The Command Processor runs on the reference clock, -- which is 32.768 kHz, and proceeds to a new state every clock cycle. Each command byte takes -- roughly two clock cycles to process. If the Command Memory is 512 bytes long, the maximum -- time taken by command processing will be 30 ms. The Command Processor does not respond to -- Initiate Command (IC) during the execution time, so the Command Transmitter must refrain -- from sending consecutive long commands to the same device without inserting sufficient dealay -- for execution. 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; constant command_acknowledge_s : integer := command_acknowledge + 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 command_acknowledge => next_state := command_acknowledge_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 and Stop Stimulus signals are asserted for one -- RCK period when we are in the start and stop stimulus states -- respectively. STARTS <= (state = start_stimulus_s); STOPS <= (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; -- 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,brightness'length)); 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. 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; -- 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. 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; -- 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. 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; -- Set the echo byte. if state = command_acknowledge_s then next_state := dec_count_s; end if; -- Initiate transmission of acknowledgement. if state = command_acknowledge_s then TXA <= true; else if TXAD then TXA <= false; else TXA <= TXA; end if; end if; -- Set the acknowledgement key. for i in 0 to 7 loop if (state = command_acknowledge_s) then ack_key(i) <= cm_out(i); else ack_key(i) <= ack_key(i); end if; end loop; -- 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,cm_addr'length)); end process; -- Stimulus Clock Generator Stimulus_Clock_Generator : process is variable il : integer range 0 to 65535 := 0; begin wait until (RCK = '1'); if STARTS then il := to_integer(unsigned(interval_len)); ISC <= true; STCK <= '0'; else if (il = 0) or (il = 1) then il := to_integer(unsigned(interval_len)); ISC <= false; STCK <= '0'; else il := il - 1; ISC <= ISC; STCK <= '1'; end if; end if; end process; -- Stimulus Generator Stimulus_Generator : process (STOPS, STCK) is variable state, next_state : integer range 0 to 4 := 0; variable sl : integer range 0 to 65535 := 0; variable il : integer range 0 to 63 := 0; variable random, next_random : std_logic_vector(7 downto 0) := std_logic_vector(to_unsigned(rck_per_ms-2,8)); begin if STOPS then state := 0; elsif rising_edge(STCK) then if (state = 0) then if ISC then next_state := 1; else next_state := 0; end if; end if; if (state = 1) then sl := to_integer(unsigned(stimulus_len)); il := rck_per_ms - 1; next_state := 2; end if; if (state = 2) then if (il = 2) then next_state := 3; sl := sl - 1; else next_state := 2; sl := sl; end if; il := il - 1; end if; if (state = 3) then if (sl = 0) and (to_integer(unsigned(stimulus_len)) /= 0) then next_state := 0; else next_state := 2; end if; sl := sl; il := rck_per_ms - 1; end if; if (state > 3) then next_state := 0; end if; state := next_state; end if; if rising_edge(STCK) then -- If RAND, we generate a new random number every STCK cycle. -- Otherwise, we set th random value to a fixed constant, so -- that the lamp pulse will be in the same place on every -- interval. if RAND then next_random(7) := random(0); next_random(6) := random(7); next_random(5) := random(6) xor random(0); next_random(4) := random(5) xor random(0); next_random(3) := random(4) xor random(0); next_random(2) := random(3); next_random(1) := random(2); next_random(0) := random(1); random := next_random; else random := std_logic_vector(to_unsigned(rck_per_ms - 2,8)); end if; -- We assert SRUN to keep the logic chip powered up. SRUN <= (state > 0); -- We assert ILP when il has the correct value. ILP <= (state = 2) and (il = to_integer(unsigned(random(4 downto 0)))); end if; SCRATCHBIT <= (state = 3); end process; -- Lamp Pulse generator Lamp_Pulse_Generator : process (STOPS, RCK) is variable pl : integer range 0 to 65535 := 0; variable prev_ilp : boolean := false; begin -- We reset the pulse generator on STOPS. We start -- a new pulse on ILP, and count down from the pulse -- length to zero on RCK. if STOPS then pl := 0; elsif rising_edge(RCK) then if ILP and (not prev_ilp) then pl := to_integer(unsigned(pulse_len)); elsif (pl > 0) then pl := pl -1; else pl := 0; end if; end if; if rising_edge(RCK) then -- We assert PA when we want the lamp to turn on, although its output -- may be modulated to reduce brightness. PA <= (pl > 0); -- We assert ENL to turn on the boost regulator only when we want the -- light to turn on. ENL <= to_std_logic(PA); -- We want to detect rising edges on ILP, so we save the previous state. prev_ilp := ILP; end if; end process; -- Ring Oscillator. R1 <= to_std_logic((SRUN or TXI or TXD) and (R4 = '0')); ring1 : BUFBA port map (R1,R2); ring2 : BUFBA port map (R2,R3); ring3 : BUFBA port map (R3,R4); ring4 : BUFBA port map (R4,R5); ROCK <= R5; -- Fast Clock. Fast_Clock : process is constant fck_length : integer := 2; variable count : integer range 0 to fck_divisor - 1 := 0; begin -- We run this state machine off the ring oscillator. wait until (ROCK = '1'); -- The fck_divisor is set at the top of the code, and should be such that -- FCK is 10 MHz and TCK, which we obtain in the next process, is 5 MHz. -- In particular, the period of TCK must be within 195-215 ns. if (count = fck_divisor - 1) then count := 0; else count := count + 1; end if; -- The Fast Clock is a negative pulse of fck_length periods of ROCK. -- We will later use the falling edge of FCK to clock TCK, and the -- rising edge to set outputs that depend upon the state of TCK. FCK <= to_std_logic((count >= 0) and (count <= fck_length)); end process; -- Transmit Clock. Transmit_Clock : process is begin -- The Transmit Clock, TCK, is FCK divided by two, but clocked by the -- falling edge of FCK, not the rising edge. wait until (FCK = '0'); if (TCK = '1') then TCK <= '0'; else TCK <= '1'; end if; 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%. 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; variable xmit_sample : boolean := false; variable xmit_ack : boolean := false; variable xmit_sample_enable : boolean := false; begin -- Clock RCK is 32.768 kHz. wait until (RCK = '1'); -- Sample transmission is enabled by the first configuration bit. xmit_sample_enable := (sensor_config(0) = '1'); -- We assert the TXEN signal when xmit_sample_enable is true -- or when we are waiting to transmit an acknowledgement. We -- use TXEN to keep the logic chip powered up. TXEN <= xmit_sample_enable or TXA; -- We divide RCK by divisor to get the sampling frequency. We run -- this counter regardless of whether transmission is enabled. if (counter < divisor - 1) then counter := counter + 1; else counter := 0; end if; -- When the counter reaches the end of a sample period, we set -- the scatter value to the lower bits of the sample value. if (counter = divisor - 1) then for i in 0 to 3 loop scatter(i) <= sample(i); end loop; else scatter <= scatter; end if; -- We transmit a sample when the scatter is equal to the counter -- and sample transmission is enabled. xmit_sample := xmit_sample_enable and (counter = to_integer(unsigned(scatter))); -- We transmit an acknowledgement when the counter is equal to -- the acknowledgement time and we have TXA. xmit_ack := TXA and (counter = ack_time) and (not CPA); -- We initiate sampling and transmission with TXI when it is time -- to transmit a sample or an acknowledgement. TXI <= xmit_sample or xmit_ack; -- We issue Transmit Acknowledgement Done (TXAD) as soon was we -- start to transmit an acknowledgement, and for only one RCK cycle. -- By this means, TXA will be cleared on the next clock cycle, -- avoiding double-tranmission of the acknowledgement. We also -- use TXAD in the Sample Transmitter to detect an acknowlegement -- transmission. TXAD <= xmit_ack; 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 := -- Determines when SCK is enabled. first_data_bit - 2; constant end_sck : integer := -- State for last SCK falling edge. start_sck + num_data_bits - 1; constant data_id_bits : std_logic_vector(3 downto 0) := std_logic_vector(to_unsigned(device_id,4)); -- The device id bits. constant aux_id_bits : std_logic_vector(3 downto 0) := std_logic_vector(to_unsigned(aux_id,4)); -- The acknowledgement id bits. constant ack_code_bits : std_logic_vector(3 downto 0) := std_logic_vector(to_unsigned(ack_code,4)); -- The acknowledgement code. variable state : integer range 0 to 63 := 0; -- Stample Transmit State variable data_bit, aux_bit : boolean := false; -- Transmit Data Bit 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 not TXI. When we first enable sample transmission, the state is -- zero. When TXI is asserted, the ring oscillator turns on, which starts -- TCK, the 5-MHz transmit clock. 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 turns off, unless it is kept running by some other -- process. With no rising edges on TCK, the state remains zero. If rising -- edges on TCK continue because the ring oscillator is still running, -- the state will remain zero so long as TXI remains unasserted. 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); -- The data bit is the outgoing bit value for data transmission. data_bit := ((state >= 0) and (state < first_start_bit)) or ((state = first_id_bit + 0) and (data_id_bits(3) = '1')) or ((state = first_id_bit + 1) and (data_id_bits(2) = '1')) or ((state = first_id_bit + 2) and (data_id_bits(1) = '1')) or ((state = first_id_bit + 3) and (data_id_bits(0) = '1')) or ((state >= first_data_bit) and (state < first_iid_bit) and SSDO) or ((state = first_iid_bit + 0) and (data_id_bits(3) = '0')) or ((state = first_iid_bit + 1) and (data_id_bits(2) = '0')) or ((state = first_iid_bit + 2) and (data_id_bits(1) = '0')) or ((state = first_iid_bit + 3) and (data_id_bits(0) = '0')); -- The ack bit is the outgoing bit value for acknowledgment transmission. aux_bit := ((state >= 0) and (state < first_start_bit)) or ((state = first_id_bit + 0) and (aux_id_bits(3) = '1')) or ((state = first_id_bit + 1) and (aux_id_bits(2) = '1')) or ((state = first_id_bit + 2) and (aux_id_bits(1) = '1')) or ((state = first_id_bit + 3) and (aux_id_bits(0) = '1')) or ((state = first_data_bit + 0) and (data_id_bits(3) = '1')) or ((state = first_data_bit + 1) and (data_id_bits(2) = '1')) or ((state = first_data_bit + 2) and (data_id_bits(1) = '1')) or ((state = first_data_bit + 3) and (data_id_bits(0) = '1')) or ((state = first_data_bit + 4) and (ack_code_bits(3) = '1')) or ((state = first_data_bit + 5) and (ack_code_bits(2) = '1')) or ((state = first_data_bit + 6) and (ack_code_bits(1) = '1')) or ((state = first_data_bit + 7) and (ack_code_bits(0) = '1')) or ((state = first_data_bit + 8) and (ack_key(7) = '1')) or ((state = first_data_bit + 9) and (ack_key(6) = '1')) or ((state = first_data_bit + 10) and (ack_key(5) = '1')) or ((state = first_data_bit + 11) and (ack_key(4) = '1')) or ((state = first_data_bit + 12) and (ack_key(3) = '1')) or ((state = first_data_bit + 13) and (ack_key(2) = '1')) or ((state = first_data_bit + 14) and (ack_key(1) = '1')) or ((state = first_data_bit + 15) and (ack_key(0) = '1')) or ((state = first_iid_bit + 0) and (aux_id_bits(3) = '0')) or ((state = first_iid_bit + 1) and (aux_id_bits(2) = '0')) or ((state = first_iid_bit + 2) and (aux_id_bits(1) = '0')) or ((state = first_iid_bit + 3) and (aux_id_bits(0) = '0')); -- We choose between the data_bit and the ack_bit depending upon whether -- this is a data or acknowledge tranmsission. if TXAD then TXB <= aux_bit; else TXB <= data_bit; end if; -- SCKEN asserts TCK onto SCK. SCKEN <= (state >= start_sck) and (state <= end_sck); -- SDI configures the ADC. SDI <= to_std_logic(state = start_sck); -- Shift the ADC bits into the sample register. for i in 0 to 15 loop if (state = end_sck - i) then sample(i) <= to_std_logic(SSDO); else sample(i) <= sample(i); end if; end loop; end process; CONV <= to_std_logic(not TXI); SCK <= to_std_logic((TCK = '0') and SCKEN); XEN <= to_std_logic(TXI and (not TXD) and (not CPA)); -- 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 <= to_std_logic(SCRATCHBIT); TP2 <= to_std_logic(ILP); -- Keeper Outputs. If we don't send these signals to an output, the compiler -- will eliminate them and all the logic behind them. In the case of STDBY, -- for example, all the standy power logic is eliminated and the chip is -- always in full-power mode. It may be that some combination of attributes -- will stop the elimination, but we have not found it yet. TP3 <= to_std_logic((STDBY = '1')); end behavior;