|
Start | Research | Competences | Projects | Publications | |||||||||
|
Optical Systems using FPGAs
OverviewBasics
Digital components and techniques play an incerasing role in optical science and optical instruments. One main area is digital image processing for image analysis and manipulation of image data. Beneath this well known area digital logic is used for control purposes, too. One solution for digital control is the use of microcontrollers which execute an algorithm in sequentiel order. Another more sophisticated method for control puporses is performed with programmable digital logic. This programmable digital logic is able to execute several actions in parallel with lower execution times than microcontrollers can reach. The use of microcontrollers due to their flexibility and easy programming features is not always the method of choice. In the case of extensive user interaction or highly complex algorithms the use of microcontrollers can be the preferred solution. But if user interaction is negligible and the control or processing algorithms are of medium or small complexity, low latency is required, programmable digital logic can be the best choise for several reasons:
The following figure shows the difference between an implementation of an algorithm with a traditional microcontroller concept and a digital logic approach [1].
The latency of a microcontroller based system is calculated with t1=12 * clock cycles and depends on the length of the algorithm to be implemented. The latency of a pure digital logic implementing the same algorithm is only t2=2 * digital gate delay << t1. The above shown simple algorithm requires only three digital logic gates, but the microncontroller in contrast is build from about several thousand logic gates. Reprogrammable digital devices stand for the best compromise between rapid prototyping and the price-power ratio. Small sized digital logic devices are GAL- and PAL-devices. They are only used for small combinational logic implementations like bus address decoders. The next level of complexity and features is reached with CPLDs (Complex Programmable Logik Device). The most powerfull devices are FPGAs (Field Programmable Gate Array). FPGAs are equipped with a high count of registers (latches) which make these devices preferrable for the implementation of state machines. CPLDs are more suited for combinational designs. But the design implementation of a complex control problem, for example a parameter specified current control of a laser diode, or digital image processing, with pure boolean algebra and basic logic gates is a high barrier and a very time consuming way. An abatract algorithm- and hardware description programming language is instead needed for the solution of such problems for converting complex algortihms into logic gates without knowledge of the hardware structure used. Programming with a high level language like VHDL is best suited for this purpose. This programming language specifies what the digital logic should perform, not how it's implemented. Synthesis programs (like compilers in traditional languages) derive a hardware dependent logic circuit from the user supplied VHDL source code. The VHDL language is similar to the imperative programming language Modula or Pascal. Parts of the solution of a problem are divided into modules. There are imperative control structures like conditional branching (if then else) or loops (for). But they have a different meaning because all code is executed in parallel! Such parallel task are described with so called processes. Fields of apllication of FPGAs in the optical design, measuring and laboratory technologies:
FPGA Design
SPACON1
SPACON1 is a universal FPGA prototype board. This board can be extended with additional IO modules, like AD- and DA convertes. I.) Schematics
II.) FigureThe following figure shows the inhouse manufactured and assembled FPGA basic board with an Xilinx Spartan-II (TM) FPGA
Click for larger image
This baisc board contains the following components:
SPADC1
SPADC1 is an addon board for the FPGA evaluation board SPACON1. It contains Analog-to-Digital and Digital-to-Analog Converters directly connected to the IO lines of the FPGA. Sampling rates upto 1 M Sample/s are provided. I.) Schematics
II.) FigureTeh following figure shows the inhous manufactured and assembled AD/DA board. This module is already connected to the above shown SPACON1 FPGA evaluation base board.
Click for larger image
This extension board contains the following components:
Example VHDL CodeThe following VHDL sourcecode performs these oeprations:
This algorithm was implemented with a Moore-like state machine. -- ==================================
-- OOOO OOOO OOOO O O OOOO
-- O O O O O O O O O
-- O O O O O O O O O
-- OOOO OOOO OOOO O OOO OOOO
-- O O O O O O O O O
-- O O O O O O O O O
-- OOOO OOOO OOOO OOOO O O OOOO
-- ==================================
-- BSSLAB, Dr. Stefan Bosse sci@bsslab.de
--
-- PROTECTED BY AND DISTRIBUTED UNDER THE TERMS OF:
-- Free Software Foundation-Europe, GNU GPL License, Version 2
--
-- $AUTHORS: Stefan Bosse
-- $INITIAL: (C) 2005 BSSLAB
-- $CREATED: 16.4.2005
-- $VERSION: 1.01
--
-- $INFO:
--
--
-- SPACON1 + SPADC
-- ADC + DAC test
--
--
-- $ENDOFINFO
--
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity adctest is
port (
CLK_EXT: in std_logic; -- CLK input from Q_Oscillator
QA: out std_logic;
QB: out std_logic;
DB: inout std_logic_vector (11 downto 0); -- Databus
DAC_CSA: out std_logic; -- Chip select DAC A
DAC_CSB: out std_logic; -- Chip select DAC B
DAC_WR: out std_logic; -- DAC Write
ADC1_RD: out std_logic; -- ADC read
ADC1_CONVST: out std_logic; -- ADC conversion start
ADC1_BUSY: in std_logic; -- ADC busy flag
ADC2_RD: out std_logic; -- ADC read
ADC2_CONVST: out std_logic; -- ADC conversion start
ADC2_BUSY: in std_logic -- ADC busy flag
);
end entity adctest;
architecture main of adctest is
signal adc_data_a: std_logic_vector (11 downto 0) := "100000000000";
signal adc_data_b: std_logic_vector (11 downto 0) := "100000000000";
signal clk_cnt: std_logic_vector (3 downto 0); -- clk divider
signal clk: std_logic; -- internal system clock
signal dummya,dummyb: std_logic;
type adc_states is (S_start,
S_conv_a,
S_busy_a,
S_rd_a,
S_wr1_a,
S_wr2_a,
S_end);
signal adc_state,adc_state_next : adc_states;
signal adc_rd_a: std_logic;
begin
clk_div: process (CLK_EXT) is
begin
if (CLK_EXT'event and CLK_EXT='1') then
clk_cnt <= clk_cnt + 1;
end if;
end process clk_div;
-- state shifter
state_shift: process (clk) is
begin
if (clk'event and clk = '1') then
adc_state <= adc_state_next;
end if;
end process state_shift;
-- state processing
state_proc: process (adc_state,
adc_data_a,
ADC1_BUSY) is
begin
case adc_state is
when S_start =>
DB <= (others => 'Z');
DAC_CSA <= '1';
DAC_CSB <= '1';
DAC_WR <= '1';
ADC1_RD <= '1';
ADC1_CONVST <= '1';
adc_rd_a <= '0';
adc_state_next <= S_conv_a;
when S_conv_a =>
DB <= (others => 'Z');
DAC_CSA <= '1';
DAC_CSB <= '1';
DAC_WR <= '1';
ADC1_RD <= '1';
ADC1_CONVST <= '0';
adc_rd_a <= '0';
adc_state_next <= S_busy_a;
when S_busy_a =>
DB <= (others => 'Z');
DAC_CSA <= '1';
DAC_CSB <= '1';
DAC_WR <= '1';
ADC1_RD <= '1';
ADC1_CONVST <= '1';
adc_rd_a <= '0';
if (ADC1_BUSY = '0') then
adc_state_next <= S_busy_a;
else
adc_state_next <= S_rd_a;
end if;
when S_rd_a =>
DB <= (others => 'Z');
DAC_CSA <= '1';
DAC_CSB <= '1';
DAC_WR <= '1';
ADC1_RD <= '0';
ADC1_CONVST <= '1';
adc_rd_a <= '1';
adc_state_next <= S_wr1_a;
when S_wr1_a =>
DB <= adc_data_a;
DAC_CSA <= '0';
DAC_CSB <= '1';
DAC_WR <= '0';
ADC1_RD <= '1';
ADC1_CONVST <= '1';
adc_rd_a <= '0';
adc_state_next <= S_wr2_a;
when S_wr2_a =>
DB <= adc_data_a;
DAC_WR <= '1';
DAC_CSA <= '1';
DAC_CSB <= '1';
ADC1_RD <= '1';
ADC1_CONVST <= '1';
adc_rd_a <= '0';
adc_state_next <= S_end;
when S_end =>
DB <= (others => 'Z');
DAC_CSA <= '1';
DAC_CSB <= '1';
DAC_WR <= '1';
ADC1_RD <= '1';
ADC1_CONVST <= '1';
adc_rd_a <= '0';
adc_state_next <= S_start;
end case;
end process state_proc;
read_a : process (clk,adc_rd_a) is
begin
if (clk'event and clk = '1') then
if (adc_rd_a = '1') then
adc_data_a(10 downto 0) <= DB(10 downto 0);
adc_data_a(11) <= not DB(11);
end if;
end if;
end process read_a;
-- dummy logic
QA <= clk_cnt(3) and ADC1_BUSY;
QB <= clk_cnt(2) and ADC2_BUSY;
clk <= CLK_EXT;
ADC2_RD <= '1';
ADC2_CONVST <= '1';
end architecture main;
PC104-to-SLI-Bus BridgeThe SLI (Simple Local Interface) -Bus servers as a simple and flexible connection of peripherial components to the PC104 embedded systems bus. The SLI-Bus provides the following data lines and control signals:
The SLI-Bus convert also implements an interval timer which can trigger system interrupts in user configured time intervalls. Fields of application:
The following picture shows an prototype implementation of this SLI-Bus converter connected on the top to a PC104 system. The bus converter was realized with a CPLD device.
Click for larger image The Implementation of the Bus-convertes was done with VHDL. The following Projectresources are available:
-- ==================================
-- OOOO OOOO OOOO O O OOOO
-- O O O O O O O O O
-- O O O O O O O O O
-- OOOO OOOO OOOO O OOO OOOO
-- O O O O O O O O O
-- O O O O O O O O O
-- OOOO OOOO OOOO OOOO O O OOOO
-- ==================================
-- BSSLAB, Dr. Stefan Bosse www.bsslab.de
--
-- PROTECTED BY AND DISTRIBUTED UNDER THE TERMS OF:
-- Free Software Foundation-Europe, GNU GPL License, Version 2
--
-- $MODIFIEDBY: BSSLAB
-- $AUTHORS: Stefan Bosse
-- $INITIAL: (C) 2005 BSSLAB
-- $CREATED:
-- $VERSION: 1.01
--
-- $INFO:
--
--
-- SD-PC104 to SLI (simple local interface) bus translator
--
-- $ENDOFINFO
Library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_arith.all;
use IEEE.std_logic_unsigned.all;
entity sli104_en is
port (
-- PC104 bus signals
RESET : in std_logic;
MEMW : in std_logic;
MEMR : in std_logic;
IOW : in std_logic;
IOR : in std_logic;
CLK : in std_logic; -- 6..8 MHz
IRQ5 : out std_logic;
IRQ3 : out std_logic;
ALE : in std_logic;
AEN : in std_logic;
SD : inout std_logic_vector(7 downto 0);
SA : in std_logic_vector(19 downto 0);
--
-- SLI bus
--
-- JA connector -> SLI data bus
SLI_DB : inout std_logic_vector(7 downto 0);
-- JB connector first 4 bits -> SLI address bus
SLI_AB : out std_logic_vector(3 downto 0);
-- JB connector last 4 bits: RD,WR,NC,NC
SLI_RD : out std_logic;
SLI_WR : out std_logic
);
end sli104_en;
architecture main of sli104_en is
--
-- PC104 bus side
--
signal clk_count : std_logic_vector (3 downto 0);
signal sd_data : std_logic_vector (7 downto 0);
signal cs_reg_timer : std_logic := '0';
signal cs_reg_conf : std_logic := '0';
signal cs_reg_id : std_logic := '0';
signal cs_sli : std_logic := '0';
signal sd_read : std_logic := '0';
signal sd_write : std_logic := '0';
signal cs_io104 : std_logic := '0';
--
-- PC104 id register
-- MSB(4): Type -> B
-- LSB(4): Revision
--
constant id_reg : std_logic_vector (7 downto 0) := X"B1";
--
-- Configuration register
--
--
-- Bit 0: Enable Interrupts
-- Bit 1: Select IRQ3/IRQ5
-- Bit 2: Enable Interval Timer with Interrupt
--
signal conf_reg : std_logic_vector (7 downto 0) := "00000000";
signal enable_irq : std_logic;
signal irq_num : std_logic;
signal enable_timer : std_logic;
--
-- Timer register
--
signal timer_reg : std_logic_vector (7 downto 0) :=
(others => '0');
-- IO address space
constant io_addr_base : std_logic_vector (15 downto 0)
:= X"0800";
constant io_addr_id_reg : std_logic_vector (15 downto 0)
:= io_addr_base + 16;
constant io_addr_conf_reg : std_logic_vector (15 downto 0)
:= io_addr_base + 17;
constant io_addr_timer_reg : std_logic_vector (15 downto 0)
:= io_addr_base + 18;
--
-- Up to 16 SLI devices can be addressed
--
constant io_addr_sli_mask : std_logic_vector (15 downto 4)
:= io_addr_base (15 downto 4);
--
-- SLI bus side
--
signal sli_read : std_logic := '0';
signal sli_write : std_logic := '0';
--
-- 1 Byte FIFO - one for each direction
-- out: from SLI to SD (read operation)
-- in: from SD to SLI (write operation)
--
signal sli_data_in : std_logic_vector (7 downto 0);
signal sli_data_out : std_logic_vector (7 downto 0);
--
-- State machine
--
type io104_states is (
S_wait,
S_read_sd,
S_read_sli,
S_read_finished,
S_write_sd,
S_write_sli,
S_write_finished
);
signal io104_state: io104_states := S_wait;
signal io104_next_state: io104_states := S_wait;
--
-- Buildin interval timer
--
type timer_states is (
S_timer_start,
S_timer_count,
S_timer_irq);
signal timer_state: timer_states := S_timer_start;
signal timer_next_state: timer_states := S_timer_start;
signal timer_count : std_logic := '0';
--
-- the counter - only the upper 8 bits are compared with
-- the timer_reg content
--
signal timer_counter : std_logic_vector (15 downto 0) :=
(others => '0');
--
-- timer clock derived from PC104 bu clock (6..8 MHz div n)
--
signal timer_clk : std_logic;
begin
--
-- clock divider
--
clk_div: process (CLK,RESET)
begin
if (RESET='1') then
clk_count <= (others => '0');
elsif (CLK'event and
CLK = '1') then
clk_count <= clk_count + 1;
end if;
end process;
--
-- CLK=8MHz => 125ns period div 16 => 2us period time
--
timer_clk <= clk_count(3);
--
-- state shifter
--
state_shifter: process (CLK,RESET)
begin
if (RESET = '1') then
io104_state <= S_wait;
elsif (CLK'event and CLK = '1') then
io104_state <= io104_next_state;
end if;
end process;
--
-- state processing
--
state_proc: process (io104_state,
IOR,
IOW,
cs_io104,
cs_sli)
begin
case io104_state is
when S_wait =>
if (IOR = '0' and cs_io104 = '1') then
sd_read <= '0';
sd_write <= '0';
sli_read <= '0';
sli_write <= '0';
io104_next_state <= S_read_sd;
elsif (IOW = '0' and cs_io104 = '1') then
sd_read <= '0';
sd_write <= '0';
sli_read <= '0';
sli_write <= '0';
io104_next_state <= S_write_sd;
else
sd_read <= '0';
sd_write <= '0';
sli_read <= '0';
sli_write <= '0';
io104_next_state <= S_wait;
end if;
when S_read_sd =>
sd_write <= '0';
sd_read <= '1';
if (cs_sli = '1') then
sli_read <= '1';
else
sli_read <= '0';
end if;
sli_write <= '0';
io104_next_state <= S_read_sli;
when S_read_sli =>
sd_write <= '0';
sd_read <= '1';
sli_read <= '0';
sli_write <= '0';
if (IOR ='1' or cs_io104 = '0') then
io104_next_state <= S_read_finished;
else
io104_next_state <= S_read_sli;
end if;
when S_read_finished =>
sd_read <= '0';
sd_write <= '0';
sli_read <= '0';
sli_write <= '0';
io104_next_state <= S_wait;
when S_write_sd =>
sd_read <= '0';
sd_write <= '1';
sli_read <= '0';
sli_write <= '0';
if (IOW ='1' or cs_io104 = '0') then
io104_next_state <= S_write_sli;
else
io104_next_state <= S_write_sd;
end if;
when S_write_sli =>
sd_read <= '0';
sd_write <= '0';
sli_read <= '0';
if (cs_sli = '1') then
sli_write <= '1';
else
sli_write <= '0';
end if;
io104_next_state <= S_write_finished;
when S_write_finished =>
sd_read <= '0';
sd_write <= '0';
sli_read <= '0';
sli_write <= '0';
io104_next_state <= S_wait;
end case;
end process;
--
-- bus read request - multiplex data on the bus
--
sd_bus_read: process (
cs_reg_timer,
cs_reg_conf,
cs_reg_id,
cs_sli,
sli_data_out,
conf_reg,
timer_reg,
sd_read)
begin
if (sd_read = '1') then
case std_logic_vector'(
cs_sli,
cs_reg_timer,
cs_reg_conf,
cs_reg_id) is
when ("1000") =>
sd_data <= sli_data_out;
when ("0100") =>
sd_data <= timer_reg;
when ("0010") =>
sd_data <= conf_reg;
when ("0001") =>
sd_data <= id_reg;
when others =>
sd_data <= (others => '0');
end case;
else
sd_data <= (others => '0');
end if;
end process;
--
-- bus write request - multiplex data from the bus
--
sd_bus_write: process (
cs_reg_conf,
cs_reg_id,
cs_sli,
cs_reg_timer,
sd_write,
SD,
CLK)
begin
if (CLK'event and CLK = '1') then
if (sd_write = '1') then
case std_logic_vector'(
cs_reg_timer,
cs_reg_conf,
cs_reg_id,
cs_sli) is
when ("0001") =>
sli_data_in <= SD;
when ("0100") =>
conf_reg <= SD;
when ("1000") =>
timer_reg <= SD;
when others =>
null;
end case;
end if;
end if;
end process;
--
-- register select
--
cs_reg_timer <= '1' when (SA(15 downto 0) = io_addr_timer_reg) and
(RESET = '0')
else '0';
cs_reg_conf <= '1' when (SA(15 downto 0) = io_addr_conf_reg) and
(RESET = '0')
else '0';
cs_reg_id <= '1' when (SA(15 downto 0) = io_addr_id_reg) and
(RESET = '0')
else '0';
cs_sli <= '1' when (SA(15 downto 4) = io_addr_sli_mask) and
(RESET = '0')
else '0';
--
-- bus tristate control
--
SD <= sd_data when sd_read = '1'
else (others => 'Z');
--
-- internal control signals
--
cs_io104 <= '1' when (cs_reg_timer = '1' or
cs_reg_conf = '1' or
cs_reg_id = '1' or
cs_sli = '1') and
(RESET = '0')
else '0';
--
-- Configuration management
--
enable_irq <= conf_reg(0);
irq_num <= conf_reg(1);
enable_timer <= conf_reg(2);
--
-- Timer
--
--
-- timer state shifter
--
timer_state_shifter: process (timer_clk,RESET,timer_next_state)
begin
if (RESET = '1') then
timer_state <= S_timer_start;
elsif (timer_clk'event and timer_clk = '1') then
timer_state <= timer_next_state;
end if;
end process;
--
-- timer state processing
--
timer_state_proc: process (timer_state,
enable_timer,
enable_irq,
timer_reg,
timer_counter)
begin
case timer_state is
when S_timer_start =>
timer_count <= '0';
if (enable_timer = '1') then
timer_next_state <= S_timer_count;
else
timer_next_state <= S_timer_start;
end if;
when S_timer_count =>
timer_count <= '1';
if (timer_counter(15 downto 8) = timer_reg) then
timer_next_state <= S_timer_irq;
else
timer_next_state <= S_timer_count;
end if;
when S_timer_irq =>
timer_count <= '0';
timer_next_state <= S_timer_start;
end case;
end process;
timer: process(timer_clk,timer_count)
begin
if timer_count = '0' then
timer_counter <= (others => '0');
elsif timer_clk'event and timer_clk = '1' then
timer_counter <= timer_counter + 1;
end if;
end process;
IRQ3 <= '1' when (enable_irq = '1' and
irq_num = '0' and
enable_timer = '1' and
timer_state = S_timer_irq) else '0';
IRQ5 <= '1' when (enable_irq = '1' and
irq_num = '1' and
enable_timer = '1' and
timer_state = S_timer_irq) else '0';
--
-- SLI bus interface side
--
SLI_AB <= SA(3 downto 0) when cs_sli = '1' else (others => '0');
SLI_RD <= sli_read;
SLI_WR <= sli_write;
--
-- SLI bus read request - multiplex data from the bus
--
sli_bus_read: process (sli_read,
SLI_DB,
CLK)
begin
if (CLK'event and CLK = '1') then
if (sli_read = '1') then
sli_data_out <= SLI_DB;
end if;
end if;
end process;
--
-- bus tristate control
--
SLI_DB <= sli_data_in when sli_write = '1'
else (others => 'Z');
end main;
Literature
|
|||||||||||||