GETTING USED TO : VHDL PROGRAMMING AND FPGAs FPGAs as the name suggests are the FIEDL PROGRAMMABLE GATE ARRAYs. In simpler words, they are a series of large number of logic gates (AND, OR etc.) which can be reprogrammed time and again with any circuit described in a HARDWARE DESCRIPTION LANGUAGE so as to serve the purpose. This .doc file will help to understand what steps to be taken in order to program the hardware of an FPGA. We start with the following diagram....... The above diagram shows IN BLOCKS what work is done.. First a source code is generated in any hardware description language which is VHDL here. Next this code is transformed into a netlist which shows the positioning and interconnection of these gates. This netlist is then mapped into groups and routed into BLOCKs known as Configurable LOGIC BLOCKs of the FPGA. Finally a BIT file is generated which contains the fusing or non-fusing of gates in the CLBs of FPGA. A 0 or 1 corresponds to closed or open switches.All these processes are carried out in the XILINX ISE 10.1 software application. The last work that remains is to physically move this BIT file into the FPGA so that its CLBs are reprogrammed in accordance with the 0s and 1s in the BIT file. This work is carried out using the XStools Utility by the Xess Corporation. WITH THIS BASIC INTRODUCTION WE ARE NOW READY TO START WITH THE INSTALLATIONS REQUIRED. STEP I : Install ISE Webpack 10.1 on your system while downloading it from the XILINX official website homepage ( http://www.xilinx.com/ise/logic_design_prod/webpack.htm) or installing it from any CD. STEP II : Install XStools Utility from the XESS official website homepage (http://www.xess.com/ho07000.html) . This utility after being installed opens a few GUI windows like GXSload, GXSport , GXStest and so on. The GXSload utility will be used later for downloading the bitstreams... In addition also download the ISE – 10.pdf a tutorial from the XESS official website. (www.xess.com) STEP III : We now start with a new project from scrap which will be used to generate an 8 BIT COUNTER. First double click the ISE 10.1 icon on the desktop. In the FILE menu select the NEW PROJECT WIZARD. Enter a name for the project and the source type as HDL and proceed next. Next select the device family, device , package and speed grade for the XESS Board used.. Here we have XSA-3S1000.Press Next thrice and the project folder is ready. Now we need to create the source code and for it a new source file. In the Sources Pane right click the XC3S1000-4ft256 object and select new source type .Enter a name in the next window and select the VHDL MODULE in the Source type on the left hand side. Press Next and the source file is ready to use. The Define Module window now appears where you can declare the inputs and outputs to the Counter circuit. In the first row, click in the Port Name field and type in clk (the name of the inputs to the Counter).Since the input clk is a one bit input hence, let the bus width remain unchecked. Next we need an output 8 bit object say b, check the bus width and select MSB as 7 and LSB as 0., so now b(7 down to 0) is used as an output object to be displayed at the BAR LEDs.Press NEXT and then FINISH. STEP IV : Next the design summary is displayed in the EDITOR pane. Double click on the TOP MODULE shown in the form of a 3 square block given by the name counter1.vhd .(the name specified in the NEW SOURCE WIZARD). Paste the VHDL code given below and then save the file. -----------------------------------------------------------------------------------------------------------------------------------------library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; ---- Uncomment the following library declaration if instantiating ---- any Xilinx primitives in this code. --library UNISIM; --use UNISIM.VComponents.all; entity Counter1 is Port ( clk : in STD_LOGIC; b : out STD_LOGIC_VECTOR (07 downto 0)); end Counter1; -- input from the GLOBAL CLOCK -- output 8 bit display architecture Behavioral of Counter1 is signal clk1: std_logic ; begin process(clk) variable cn1: integer := 0 ; begin if (rising_edge(clk)) then clk1 <= '0'; if (cn1=49999999) then clk1<='1'; --Since input clk is 50MHz. So cn1 :=0; else cn1 := cn1 + 1; end if; end if ; end process; process(clk1) variable c: std_logic_vector(7 downto 0) := "00000000"; begin if(rising_edge(clk1)) then c := c + "00000001" ; if(c = "11111111") then c:="00000000"; enD if; end if; b<= c; end process; end Behavioral; Next step is to check the Syntax and Synthesize the design. In the processes pane, press the Synthesize-XST tab and double click on Check Syntax. Check for errors if any and then compile again by clicking on the same Option. Next double click on SYNTHESIZE –XST tab. Now we are ready to check the simulation of the program to see how it behaves. In the NEW SOURCE WIZARD select the TEST BENCHFORM option and then give a filename say Counter3. Set the option of the window that pops up as given below and click finish. A new file Counter3.tbw is generated now. Save this file. Now as shown above Select the BEHAVIORAL SIMULATION in the Sources pane. Highlight the .tbw file in it. Now in the processes pane go to XILINX ISE SIMULATOR -> SIMULATE BEHAVIORAL MODEL. We should get the following benchform for the design.. This benchform is for a 4 bit counter. STEP V : The Synthesize function generates a NETLIST of the VHDL code. This NETLIST has to be mapped and routed into the CLBs of the FPGA. For this we need to specify which pins of FPGA accept INPUTs and display OUTPUTs. We mean to form a new file known as “USER CONSTRAINT FILE” (.ucf) which associates the .vhd file to the pins of FPGA. RIGHT CLICK on the top module and select new source. In the window that appears, select the IMPLEMENT CONSTRAINTS FILE option in the left hand side and give a name to the file say counter2 to it. Click finish twice and a new file is generated by the name Counter2.ucf . We need to fill this file with the pins of FPGA. HERE we have mounted the XSA board on the XStend V 4.0 board which looks like below. The XStend Board has a 10 segment BAR LED display which will be used to display the output from the XILINX FPGA on the XSA Board. The XSTend + XSA3S1000 board connections are given with this document file and they are used here to assign pin connections. We require 1 pin(for clk input) and 8 pins ( for Bar graph output) i.e. 9 pins in all. Select the User Constraints Tab in the Processes pane . Click on Floorplan IO- PRE SYNTHESIS option. A new window (XILINX PACE) appears now which looks like --------- The diagram above shows the pin configurations of some other circuit and how all INPUTs and OUTPUTs are assigned. For our COUNTER design, the pin connections are as CLK Input - P8 -- This pin goes to the inbuilt Clock on board - 50 MHz. B(0) Output - L5 -- The next 8 pins are those of the BAR LED display. B(1)Output - N2 B(2)Output - M3 B(3)Output - N1 B(4)Output - T13 B(5)Output - L15 B(6)Output - J13 B(7)Output - H15 Fill these pin numbers in the Window and then press the Save option and let the default selected option remain checked in the window that appears. Press OK. STEP VI : Now we need to implement this design that is map and route the gates assigning them to CLBs. Right clicking on the IMPLEMENT tab in the PROCESSES pane does it automatically. Finally right click on GENERATE bitstream option in the same PROCESESS pane and select PROPERTIES. Now select the Configuration Options tab in the Process Properties window and set all the Pull Up and Pull Down values to Float to disable the internal resistors of the FPGA. (At the minimum, the Unused IOB Pins value must be set to Float, but it is best to set them all to Float.) If they are not disabled, the strong internal pull-up and pull-down resistors in the Spartan3 FPGA will overpower the external resistors on the XSA Board. Press OK and then double click on GENERATE PROGRAMMING FILE option. In a short while the final bitstream is generated, which is ready to be downloaded into the FPGA. STEP VII : Downloading the Bitstream The bitstream is downloaded into a physical FPGA chip (usually embedded in some larger system). The electronic switches in the FPGA open or close in response to the binary bits in the bitstream. Upon completion of the downloading, the FPGA will perform the operations specified by your HDL code or schematic. The XSTOOLs from XESS provide utilities for downloading the bitstream into the FPGA on the XSA Board. The GSXLOAD utility is used to download bitstreams into the FPGA. (Refer to manual on XSTOOLS for more detail). The XSA Board is powered with a DC power supply and is attached to the PC parallel port with a standard 25-wire cable as shown below Double clicking on the icon brings up the gxsload window: Then click in the Board Type field and select XSA-3S1000 from the drop-down menu to load with the bitstream. Then just drag the .bit file from the Project folder and put it in the FPGA/CPLD box. The bitstreams are downloaded into the FPGA within a minute and the FPGA is then programmed to function accordingly. IF THE BAR LED IS GIVEN A LOOK IT WILL SHOW THE BINARY 8 BIT COUNTER WORKING . THIS IS HOW AN FPGA IS PROGRAMMED. THE ACCOMPANYING FOLDER ALSO CONTAINS VHDL CODE FOR DESCRIBING THE WORK OF “UART”, A KEYBOARD, AND THE VGA PORT present on the board. FOR MORE WORK please refer to the manual ISE-10.pdf PROGRAMMES DEVELOPED:1. SERIAL PORT INTERFACING USING PC i.e. development of a UART on the kit :UART is universal asynchronous receiver transmitter which handles the serial port communication by converting parallel data into serial form, adding stop and start bits, adding parity bits etc. The same UART has been coded using VHDL and has been downloaded on the FPGA. MAX-232 is a level converter present on the kit which converts the digital data given by UART into RS-232 standard format. The following code receives a byte sent through the serial port of PC using SERCOM or HYPERTERMINAL and echoes back and send the same to the PC’s serial port. THE SUPPOSED MODEL SHOULD LOOK LIKE THIS The following VHDL contains a main VHDL file uart.vhd in which three other modules has been instantiated. 1. CLKPROVIDE.VHD :- It provides a pulse of required low time and one main clock’s time period hightime. 2. RECEIVER.VHD :- It acts as a receiver on the kit i.e. it takes the serial data sent to the kit through the PC and converts it into parallel data. 3. TRANSMITTER.VHD :- It acts as a teansmitter on the kit i.e. it converts the parallel data fed to into serial data, adds stop and start bits and send it to PC. UART.VHD --------------------------------------------------------------------------------------------------------------------------------------entity uart is Port ( clk : in STD_LOGIC; reset : in STD_LOGIC; rxd : in STD_LOGIC; txd : out STD_LOGIC; led : out STD_LOGIC_VECTOR ( 7 downto 0)); end uart; architecture Behavioral of uart is component clkprovide is generic( DIVISOR:INTEGER); Port ( clk : in STD_LOGIC; clka : out STD_LOGIC); end component; component receiver is Port ( clk : in STD_LOGIC; clk_rx : in STD_LOGIC; reset : in STD_LOGIC; reada : in STD_LOGIC; -- reada is a signal which can be used as enable switch for receiver in any program. Here receiver is always ready therefore reada is always high. rxd : in STD_LOGIC; datao : out STD_LOGIC_VECTOR (7 downto 0); rxbyte_av : out STD_LOGIC); end component; component transmitter is Port ( clk : in STD_LOGIC; clk_tx : in STD_LOGIC; reset : in STD_LOGIC; Rdy : in STD_LOGIC; txdata : in STD_LOGIC_VECTOR (7 downto 0); txd : out STD_LOGIC; tx_rdy: out std_logic); -- tx_rdy is a signal which tells when transmitter is ready to take a value and transmit it end component; signal clk_rx,clk_tx:std_logic; signal datao,txdata:std_logic_vector(7 downto 0); signal rxbyte_av,rdy,tx_rdy:std_logic; signal high:std_logic:='1'; begin txdata<=datao; led<=datao; rdy<=not rxbyte_av; -- rdy signal acts as enable switch for transmitter which depends on the rxbyte_av RECEIVER_CLOCK_GENERATOR: clkprovide generic map ( DIVISOR => 2604 ) port map ( clk,clk_rx); RECEIVER_MODULE: receiver port map (clk,clk_rx,reset,high,rxd,datao,rxbyte_av); rxbyte_av tells when the whole byte is received -- TRANSMITTER_CLOCK_GENERATOR: clkprovide generic map ( DIVISOR => 5208 ) port map ( clk,clk_tx); TRANSMITTER_MODULE: transmitter port map ( clk,clk_tx,reset,Rdy,txdata,txd,tx_rdy); end Behavioral; --------------------------------------------------------------------------------------------------------------------------------------This was the UART.VHD code. It is commented and cab be read for smaller details. Now the 3 modules are as follows:1. CLKPROVIDE.VHD This takes main input clock ‘clk’ as input and counts the number of rising edges of this clk. When this count becomes equal to DIVISOR then it gives a pulse for 1 main clock’s time period and then again start counting from zero. entity clkprovide is generic ( DIVISOR : INTEGER :=2); Port ( clk : in STD_LOGIC; clka : out STD_LOGIC); end clkprovide; architecture Behavioral of clkprovide is begin process(clk) variable cnt:INTEGER:=0; variable count1:INTEGER:=DIVISOR; begin if rising_edge(clk) then clka<='0'; if cnt=0 then cnt:=count1-1; clka<='1'; else cnt:=cnt-1; clka<='0'; end if; end if; end process; end Behavioral; --------------------------------------------------------------------------------------------------------------------------------------- 2. RECEIVER.VHD This is a receiver module. It takes data from rxd port of serial port and cuts off the start and stop bits and convert it into parallel data and stores it into a buffer named datao. Also when the whole byte is available it makes rxbyte_av signal high for a single clk period so that another module can attach to it through this rxbyte_av. The whole process of storing data takes place at the rising edge of clk_rx clock which is of frequency 9600*2 Hz ( generated by CLKPROVIDE.VHD). Since data arrives at 9600 bps, therefore this clk_rx helps in getting data somewhere in the middle of bit time. entity receiver is Port ( clk : in STD_LOGIC; clk_rx : in STD_LOGIC; reset : in STD_LOGIC; reada : in STD_LOGIC; rxd : in STD_LOGIC; datao : out STD_LOGIC_VECTOR (7 downto 0); rxbyte_av : out STD_LOGIC); end receiver; architecture Behavioral of receiver is signal rxbuffer: std_logic_vector (7 downto 0); begin process( reset, clk , clk_rx, rxd) variable bitpos:INTEGER:=0; variable count: INTEGER:=0; begin if ( reset='0') then rxbuffer <= "11111111"; bitpos:=0; rxbyte_av<='0'; elsif rising_edge(clk) then case bitpos is when 0 => count:=0; rxbyte_av<='0'; if rxd = '0' then -- start byte is detected rxbuffer<="11111111"; bitpos:=1; end if; when 1 => if reada= '1' then if clk_rx='1' then if rxd='0' then -- start byte is confirmed and we can proceed further bitpos:=2; else bitpos:=0; -- start byte was just an spike. end if; end if; end if; when 10 => if ( clk_rx ='1' ) then -- End byte is detected bitpos:=0; datao<= rxbuffer; rxbyte_av<='1'; end if; when others => if reada ='1' then if clk_rx = '1' then if (count =1) then count:= 0; --rxbuffer<= rxd & rxbuffer( 7 downto 1); rxbuffer( bitpos-2)<= rxd; -- shifting data into a buffer on each receiver clock bitpos:= bitpos+1; elsif count=0 then count:=1; end if; end if; end if; end case; end if; end process; end Behavioral; 3. TRANSMITTER.VHD This is a transmitter module which takes parallel data txdata and convert it into serial data txd. Also it outputs a signal named tx_rdy which shows that whether transmitter is ready to accept a new byte or not. This whole process of sending data is done using clk_tx clock which is of frequency 9600 Hz and sends data at the same speed. entity transmitter is Port ( clk : in STD_LOGIC; clk_tx : in STD_LOGIC; reset : in STD_LOGIC; Rdy : in STD_LOGIC; txdata : in STD_LOGIC_VECTOR (7 downto 0); txd : out STD_LOGIC; tx_rdy: out std_logic); end transmitter; architecture Behavioral of transmitter is signal txbuff:STD_LOGIC_VECTOR(7 downto 0); begin process(Rdy,reset,clk) variable counter:INTEGER; type state_type is (mark,sendstart,shift,stop); variable state:state_type:=mark; begin if reset='0' then txbuff<="00000000"; counter:=0; TxD<='1'; state:=mark; elsif rising_edge(clk) then case state is when mark => counter:=0; tx_rdy<='1'; txd<='1'; if Rdy='0' then txbuff<=txdata; tx_rdy<='0'; state:=sendstart; end if; when sendstart => if clk_tx='1' then TxD<='0'; state:=shift; end if; -- check for ready -- not busy -- transmitter is busy when shift => if clk_tx='1' then TxD<=txbuff(0); txbuff(6 downto 0)<= txbuff(7 downto 1); tx_rdy<='0'; -- transmitter is busy counter:=counter+1; if counter=8 then counter:=0; state:=stop; else state:=shift; end if; end if; when stop => if clk_tx='1' then TxD<='1'; tx_rdy<='1'; state:=mark; end if; end case; end if; end process; end Behavioral; --------------------------------------------------------------------------------------------------------------------------------- 2.KEYBOARD INTERFACING through PS2 port of the kit :The Keyboard gives two inputs to the FPGA which are psclk and psdata. Whenever a key is pressed a make code is send to the FPGA and when key is released a scancode F0 along with the break code is sent to the FPGA. Whenever data is to be sent by the keyboard firstly the psdata goes low which is the start bit and then on every falling edge of psclk the data is retrieved. Refer ISE-10 tutorial for more information on keyboard interfacing. Keyboard can be used in two modes. This code is for that mode in which only the keyboard has to send data to the CPU. The commented VHDL code for the same is as follows. entity keyboard is generic (CLKFREQ : INTEGER := 50_000_000; PSCLKFREQ : INTEGER := 10_000 ); Port ( RST : in STD_LOGIC; Clk : in STD_LOGIC; psClk : in STD_LOGIC; psdata : in STD_LOGIC; scancode : out STD_LOGIC_VECTOR (07 downto 0); scan_rdy : out STD_LOGIC); end keyboard; architecture Behavioral of keyboard is signal sc_r : STD_LOGIC_VECTOR(09 downto 0); signal sc_rdy : STD_LOGIC; signal rdy : STD_LOGIC; constant TIMEOUT : natural :=(CLKFREQ/PSCLKFREQ); constant KEYRELEASE : STD_LOGIC_VECTOR := "11110000"; signal key_rel : STD_LOGIC; begin process(RST, psClk) begin if RST = '0' then --- this reset will have an effect only when RST = '0' till one falling_edge of psClk is detected sc_r <= (others => '0'); elsif falling_edge(psClk) then sc_r <= psdata & sc_r(sc_r'high downto 1); end if; end process; --scancode <= sc_r(scancode'range); process(Clk, RST) variable cntr : INTEGER := 0; begin if RST = '0' then cntr := 0; sc_rdy <= '0'; elsif rising_edge(Clk) then sc_rdy <= '0'; -- sc_rdy is high just for only 1 pulse. if psClk = '0' then cntr := 0; -- this statement resets the counter automatically when psClk is LOW that is , whenever it detects any pulse elsif cntr /= TIMEOUT then -- thsi is also important cntr := cntr + 1; if cntr = (TIMEOUT - 1) then sc_rdy <= '1';-- sc_rdy is a flag that indicates that scancode is ready that it signals all make and break codes have come. end if; end if; end if; end process; process(Clk) begin if rising_edge(Clk) then rdy <= '0'; if sc_rdy = '1' then -- pulse at sc_rdy is generated twice 1) when keyrelease is detected 2)when break code is sent if sc_r(07 downto 0) = KEYRELEASE then key_rel <= '1'; elsif key_rel = '1' then rdy <= '1'; scancode <= sc_r(scancode'range); key_rel <= '0'; end if; end if; end if; end process; scan_rdy <= rdy; end Behavioral; 3.VGA port interfacing and displaying a counter on VGA monitor screen :VGA port requires 3 analog signals RED,GREEN and BLUE and 2 digital signals VSYNC and HSYNC from FPGA to drive a VGA monitor. The required timings of VSYNC and HSYNC and the required data can be seen from the XESS application note. Refer to the XESS tutorial for more information on the VGA port. The following VHDL code contains a clka clock which is generated from clk 50 MHz. Thus clka has a time period of 40 ns which is best suited for the VGA control. The entity shown below has three inputs RED,GREEN and BLUE from DIP switches which drive the output signals RED_OUT,GREEN_OUT and BLUE_OUT. These are 3 bit signals each which are sent to DAC present on the kit to convert them to the analog signals. The following VHDL code has H_COUNT and V_COUNT which keep track of the pixel and these are used to define coordinates on the VGA screen which are to be filled with colour. The second process is a 1sec counter process. It has 1 module clkprovide.vhd described above. The VHDL code is as follows :entity vga is Port ( clk : in STD_LOGIC; red : in STD_LOGIC; green : in STD_LOGIC; blue : in STD_LOGIC; red_out : out STD_LOGIC_VECTOR( 2 downto 0 ); green_out : out STD_LOGIC_VECTOR( 2 downto 0 ); blue_out : out STD_LOGIC_VECTOR( 2 downto 0 ); h_sync : out STD_LOGIC; v_sync : out STD_LOGIC); end vga; architecture Behavioral of vga is component clkprovide is MHz -- provides a clock of frequency= 50/DIVISOR generic ( DIVISOR : INTEGER :=2); Port ( clk : in STD_LOGIC; clka : out STD_LOGIC); end component; signal clka:std_logic; signal v,video_on, video_h,video_v:std_logic; signal h_count,v_count:std_logic_vector( 9 downto 0 ) :="0000000000"; signal flag:std_logic:='0'; signal cnt,dig:INTEGER:=0; signal no:std_logic_vector(6 downto 0):="1111100"; -- LED 7 segment display information begin A1: clkprovide generic map( DIVISOR =>2) port map ( clk,clka); process(clka) variable dis:INTEGER; begin if rising_edge(clka) then if h_count= 799 then h_count<="0000000000"; else h_count<=h_count+"0000000001"; end if; -- provideing clock of time period 40 ns if h_count>659 and h_count<=755 then h_sync<='0'; else h_sync<='1'; end if; if v_count = 524 and h_count = 799 then v_count<="0000000000"; elsif h_count= 799 then v_count<=v_count+"0000000001"; end if; if v_count>=493 and v_count<=494 then v_sync<='0'; v<='0'; else v_sync<='1'; v<='1'; end if; if h_count<=639 then video_h<='1'; else video_h<='0'; end if; if v_count<=479 then video_v<='1'; else video_v<='0'; end if; -- The following conditions help to make a LED 7 segment display -- These 7 if-else conditions shows 7 different areas of the VGA monitor if (v_count>110 and v_count<130 and h_count>270 and h_count<370) then if no(0)='1' then video_on<='1'; else video_on<='0'; end if; elsif (v_count>230 and v_count<250 and h_count>270 and h_count<370) then if no(6)='1' then video_on<='1'; else video_on<='0'; end if; elsif (v_count>350 and v_count<370 and h_count>270 and h_count<370) then if no(3)='1' then video_on<='1'; else video_on<='0'; end if; elsif (v_count>250 and v_count<350 and h_count>250 and h_count<270) then if no(4)='1' then video_on<='1'; else video_on<='0'; end if; elsif ( v_count>130 and v_count<230 and h_count>250 and h_count<270) then if no(5) = '1' then video_on<='1'; else video_on<='0'; end if; elsif (v_count>130 and v_count<230 and h_count>370 and h_count<390) then if no(1)='1' then video_on<='1'; else video_on<='0'; end if; elsif (v_count>250 and v_count<350 and h_count>370 and h_count<390) then if no(2)='1' then video_on<='1'; else video_on<='0'; end if; else video_on<='0'; end if; end if; end process; red_out(0)<= red and video_on; red_out(1)<= red and video_on; red_out(2)<= red and video_on; green_out(0)<= green and video_on; green_out(1)<= green and video_on; green_out(2)<= green and video_on; blue_out(0)<= blue and video_on; blue_out(1)<= blue and video_on; blue_out(2)<= blue and video_on; process(clk) variable cnt:INTEGER:=0; begin if rising_edge(clk) then cnt:=cnt+1; if cnt=50000000 then dig<=dig+1; cnt:=0; end if; if dig=10 then dig<=0; end if; end if; end process; -- no is a signal which is a LED display information no<="0111111" when dig= 0 else "0000110" when dig=1 else "1011011" when dig=2 else -- 1 sec counter "1001111" when dig=3 else "1100110" when dig=4 else "1101101" when dig=5 else "1111101" when dig=6 else "0000111" when dig=7 else "1111111" when dig=8 else "1100111" when dig=9 else "1010011" ; end Behavioral;