FPGA Essentials: Basys 3 Artix-7 FPGA - Review

Table of contents

RoadTest: FPGA Essentials: Basys 3 Artix-7 FPGA

Author: dubbie

Creation date:

Evaluation Type: Development Boards & Tools

Did you receive all parts the manufacturer stated would be included in the package?: True

What other parts do you consider comparable to this product?: Xilinx and partners provide a range of alternative boards designed for introducing users to the possibilities of their FPGAs, such as the AES-A7MB-7A35T-G - Evaluation Kit, Artix-7 35T FPGA, Digilent Pmod Compatible, Low Power (£82.03) and the 410-279 - Development Board, Zynq-7000 ARM/FPGA SoC, On Board FT2232HQ USB-UART Bridge Controller (£136.00), which are placed either side price-wise of the Basys 3 Artix-7 FPGA, 7-Segment Display, 16 User Switches, 16 User LED'S Development Board (£108.00) used in this Road Test. Each of the development boards is specific to a family of Xilinx FPGA families so if you are interested in a particular FPGA type then there is not always that much choice. The 10-183 - Development Board, Basys 3 Artix-7 FPGA is particularly useful for introductory activities as it has the 7 segment displays, LEDs and switches integrated into the board, as well as the PMOD connectors.

What were the biggest problems encountered?: The biggest problem encountered was the complexity of the Vivado webpack environment. It takes a longish time to download and install (although this is a one-off time delay),and it seems excessively slow to perform most of the actions requested. It is a large package and it performs many complex actions, mainly in the background, so this might be understandable. It may also be that a more powerful laptop would be better. Vivado is essentially an industrial package and is aimed at full-time, experienced users. For the occasional and/or novice user it is a real challenge. The VHDL language itself is a challenge to get to grips with and this when combined with the complexity of Vivado creates what could be called a 'perfect storm'. The development board itself is simplicity.

Detailed Review:

Introduction

 

For my Road Test of BASYS3 development board I had decided to use it to illustrate the process for designing and programming a Simple Instruction Set Computer (SISC) although this would only be the microprocessor part. By using an FPGA it is possible to design the hardware architecture of the microprocessor with an idealised approach, rather than having to consider realistic restrictions such as memory address and data bus being in multiples of 8 bits. I wanted to design and implement my own microprocessor based on the Reduced Instruction Set Computer (RISC) philosophy. This philosophy can be a bit confusing as most people would consider this to mean a smaller number of instructions, whereas the reality is that it means each instruction is simplified. One of the effects of this simplification process is that the total number of instructions can be significantly increased. To try and make my approach a bit easier to understand I have decided to call it a simplified instruction set computer (SISC). The FPGA used in this PCB is an Artix 7 XC7A35T which has approximately 4000 registers which is not that many and in all likelihood not all of them will be accessible anyway, due to routing constraints. The XC7A35T does contain other elements in addition to the registers, such as DSP (Digital Signal Processing) slices, memory, I/O (Input/Output) but my initial approach was not be using any of them. My main aim was to make it as easy as possible to understand the design of a simple microprocessor and subsequent implementation and testing.

 

The first step in my Road Test of the Digilent BASYS3 was to download the Vivado software from Xilinx. This step did not go very well. Xilinx do seem to make their web site and software download process a little bit confusing and there are only a few references to the free web pack version of Vivado that can be used with the Digilent BASYS 3. Even when the correct link is found the download link states you need to have a FLEX licence, which of course I did not have. Anyway, I persisted and eventually found and downloaded the correct software. Again, it did not go that smoothly as it took 4 hours and 38 minutes just to download (where did my internet speed disappear to?). It then took 63 hours and 23 minutes to install, but this was mainly my fault as my laptop goes to sleep if you stop pressing keys, so every now and then I would wander in, wake it up, let install a bit more, get bored and walk away again, when it then went back to sleep again. Still the software did eventually download. It does take a long time to get going but perhaps my laptop is a bit on the slow side, taking 1 minute and 46 seconds before the splash screen is shown. It is probably talking to some cloud somewhere to see if I am a legitimate user. This is a bit disconcerting as nothing seems to be happening during this period so the temptation is to think it isn't started and start clicking icons and pressing buttons - which doesn't actually help. This delay does seem to be a recurring feature of the Vivado software as the indicators that the software is busy doing something are not that obvious and several times I thought I had not correctly clicked on something so I clicked again, only to find that it was being repeated or doing several different things, apparently at the same time. I had to learn to be patient and ensure a command had completed before starting the next step

 

For the SISC design I wanted to implement I think there were three main stands:

 

1) Getting to grips with Vivado (which was difficult).

2) Designing the structure of the microprocessor (really quite simple).

3) Implementing the microprocessor design in VHDL and Vivado (hair-tearing out difficult).

 

Designing a Simplified Instruction Set Microprocessor (or Computer)

 

My first consideration was to think about the instruction set, at a binary or machine code level. Microprocessor instructions at this level are comprised of two elements

 

Opcode and Operands

 

The approach will be simplicity at all times so each instruction will be atomic and by this I mean it will not be divisible or sub-divided into different categories. For example, a memory move instruction can be direct access to memory, or indirect access to memory where a register is used to hold the memory address. In SISC this will be considered to be two different instructions. This simplifies the decoding of the opcode allowing a simple binary encoding to be used. This means that the number of instructions is a binary multiple. So for a 1 bit opcode that will be only two instructions, for example NOOP and JUMP (no-operation and absolute jump to the specified programme memory location). If two bits are used for the opcode then there are four instructions and so on. You might be interested to know that the theoretical minimum number of instructions a microprocessor needs is only one. It is some complicated conditional jump instruction which I have currently forgotten. Microprocessors only have more than one instruction in order to make it simpler to write the programmes.

 

A useful number of instructions is between 32 and 64 so the opcode can be 5 bits or 6 bits but in the end I only used 2 bits for the opcode and so only had four instructions. As a FPGA was being used to implement the design we can make the number of bits for the opcode anything we want, at anytime in the design. For realistic microprocessor design, instructions usually tend to be some multiple of 8 bits as this was the traditional memory chip bus width, but this can limit the number of bits available for the opcode and the corresponding operands. As I am planning to implement the memory directly with the FPGA as well, then we can have memory locations of any bit width we want. But in the end I ended up using an 8 bit instruction length as it was enough for what I wanted.

 

Microprocessor Instructions can be divide into four categories:

 

1) No operands. These are typically hardware based such as NOOP. There are not many of this type.

2) One operand. An example of this would be INCR to increment a register. There are several useful one operand instructions.

3) Two operand instructions. This tends to be the majority of instructions and the most common would be a register to register move such as MOVR R1, R2.

4) Three operand instructions. These are the operator type of instructions such as add. An example might be ADDR R1, R2, R3. With this instruction the contents of register R1 is added to the contents of register R2 and the results placed into register R3, leaving the contents of R1 and R2 unchanged.

 

There are not many microprocessors that use three operand instructions as it makes the instructions much longer. For example, a microprocessor with 256 instructions and 256 registers needs an 8 bit opcode and 3x8 = 24 bits of operand, making a total of 32 bits.

 

For the SISC approach the tendency is to use registers for the operands and not memory locations because memory operations slow down execution speeds. This results  in only two instructions that access memory, SAVE and LOAD. The syntax of these instructions would be

 

SAVE Rx, memlocation   ; which saves the contents of register Rx into the specified memory location directly.

LOAD memlocation, Rx   ; which moves the contents of the specified memory location into register Rx.

 

x has the value 0 to the maximum number of registers, which again, would be a binary multiple.

 

Implementing the Design

 

Now to turn the design into VHDL. I started with the simplest microprocessor possible which just has two instructions NOOP and JUMP. I decided initially to use 2 bits for the opcode and then that was as far as I progressed. The NOOP does not have any operands so the rest of the bits in those instructions are unused. The JUMP instruction uses the remaining bits for the absolute address to jump to. I wanted enough to demonstrate correct operation but not too many to make it confusing, so I settled on an 8 bit instruction. The JUMP this allowed 6 bits for the memory address and created a programme memory space of 64 locations. This simple design does not have any data memory and cannot really do anything much except waste time and jump about in the programme memory. A simple programme could be something like that below.

 

START:   NOOP

                NOOP

                NOOP

                JUMP START

 

This could be executed with the VHDL listed below:

 

-- RISC Design
-- Dubbie 24th May'18
--

library ieee;
use ieee.std_logic_1164.all;
use work.std_arith.all;

entity risc_detector is
port (clk : in std_logic; 
      data : in std_logic_vector(7 downto 0);
      pc : buffer std_logic_vector(5 downto 0);
end risc_detector;

 

architecture struct of risc_detector is


begin

process

constant  noop : std_logic_vector(1 downto 0) := "00";    
constant  jump : std_logic_vector(1 downto 0) := "01";
constant  reset: std_logic_vector(5 downto 0) := "000000";

 

begin
  wait until (clk = '1' and clk'event);
  case data(7 downto 6) is
  when noop =>
   pc <= pc + 1;
  when jump =>
   pc <= data(5 downto 0);
  when others =>
    pc <= reset;
  end case;
end process; 
end struct;

 

However, VHDL is a strongly typed programming language and the statement to increment the programme counter (pc) which is std_logic_vector with an integer increment (pc <= pc + 1) doesn't work with the standard IEEE library. It didn't work with the NUMERIC package either so eventually I resorted to implementing a binary counter using Boolean statement, as listed below:

 

  when noop =>

--   pc <= pc + 1;

    pc(0) <= not pc(0);

    pc(1) <= pc(1) xor pc(0);

    pc(2) <= pc(2) xor (pc(1) and pc(0));

    pc(3) <= pc(3) xor (pc(2) and pc(1) and pc(0));

    pc(4) <= pc(4) xor (pc(3) and pc(2) and pc(1) and pc(0));

    pc(5) <= pc(5) xor (pc(4) and pc(3) and pc(2) and pc(1) and pc(0));

 

I know that I can fix this problem but I had so much difficulty with Vivado that in the end I opted for simplicity in order to make progress.

 

Using Vivado

 

As the BASYS 3 board is targeted at the newcomer to VHDL and FPGAs I tried to take that approach to the design of the VHDL and the implementation using Vivado. However, my experience was that without some prior knowledge and experience, trying to teach yourself is very difficult, confusing and frustrating. I often just wanted to ask an experienced person what my current problem was. There is an extensive community and Xilinx do offer assistance but it would be with significant time delays, and I just wanted to get on and learn by myself. Eventually, I started to make some progress and then ran out of time. There are many hundreds of training videos and YouTube videos for VHDL and for Vivado and even some for BASYS 3. If you have plenty of time and concentration I am sure that they will provide you with all the understanding and knowledge needed. The ones that I found the most help were YouTube videos for Vivado by augmented startups, see below:

 

 

These videos went through the whole process at a speed I could cope with and at a level of detailed step-by-step instruction that eventually enabled me to make some progress.

 

I think it helped to have understanding of the software process for FPGAs so I have listed what I think is the sequence below:

 

1) Compiling.

2) Synthesis.

3) Simulation.

4) Associating FPGA I/O pins with specific VHDL variables.

5) Place and Route, also known as post-synthesis implementation.

6) Downloading to the target board.

 

Compiling

 

For software engineers this is the standard step of converting the code into executable instructions, except for FPGAs it isn't executable and it isn't sequentially. It is converting the programme into a hardware representation, which is operating all the time (that is, concurrently). Coming from a microprocessor background this is one of the things about VHDL that I initially found most confusing. In Vivado I found it even more confusing as there doesn't seem to be an explicit compilation step. It does exist but it happens automatically when synthesis is selected, which does make sense now, but not initially. Instead what happens is that the editor shows syntax errors by underlining the instructions, see below.

 

 

Not at all obvious and I just ignored it as it is something you see when programmes are copied into Word. It is not very clear but there is a syntax error in red unlined statement, as there is a missing close bracket. Also, unlike other VHDL packages I have used, you can still proceed to the synthesis step even if there are syntax errors and the compilation fails. Instead you get an error message indicating that there is no HDL source file, and as you are staring at the programme listing within Vivado, this is difficult to understand. I went down many unintended avenues of fruitless discovery until I did something that just refused to go away. Something to do with replacing a file with itself, which it wouldn't let me do or undo. Eventually I just gave up and made another version of the Project, which solved that problem.

 

Synthesis

 

Because VHDL is aimed at creating hardware the simulation stage is very important as that is when you can determine if you design works as required. Simulation in Vivado is in two different ways. The first is a functional simulation, called Behavioral, which uses idealised logic and this is achieved using the Simulation step within Vivado. Vivado identifies this step as Simulation, again working on the concept that separate steps within the software are not needed, but it does slow everything down. For simulation to work then simulated inputs are needed and Vivado uses Test Benches for this. I have not used Test Benches with VHDL before, having used waveform simulators, so I did find this step rather confusing. Especially as the Test Bench is simply another VHDL programme that creates the inputs for the VHDL programme under test. It actually looked quite easy to create the Test Bench and initially I tried this. But, although I could create a Test Bench that would compile it would not create the correct input signals for the VHDL programme being tested. I eventually gave up on making my own Test Bench and used an automatic Test Bench generated as recommended by Augmented Startups (see above). This uses a free web site created by Doulos (a well known provider of VHDL packages and training), which solved all my test bench problems. See below:

 

 

Now that I had a compiling VHDL programme and a working VHDL Test Bench I could actually get to the simulation. Below is a fragment from the Test Bench listing the values to be applied to the SISC VHDL programme.

 

uut: SISCnopjmp1 port map ( clk  => clk,
                              data => data,
                              pc   => pc );

  stimulus: process
  begin
 
    -- Put initialisation code here
  data <= "11000000";
  wait for 150 ns;
 
  data <= "01000001";
  wait for 200 ns;
 
  data <= "00000000";
  wait for 400 ns;
 
    -- Put test bench stimulus code here

    stop_the_clock <= true;
    wait;
  end process;

  clocking: process
  begin
    while not stop_the_clock loop
      clk <= '0', '1' after clock_period / 2;
      wait for clock_period;
    end loop;
    wait;
  end process;

 

As well as VHDL being a language for implementing hardware and is essentially concurrent, it is the only programming language that I have used that incorporates the concept of time directly.

 

The Test Bench fragment above creates input values for the SISC VHDL programme. The SISC VHDL only has two inputs, one is the instruction machine code, called data and the other is the clock, called clk. So that data values which I have listed in binary to make them easier to understand are the instructions for the SISC microprocessor to execute. The first one I have given is

 

11000000

 

which is an undefined instruction and creates a software reset. I had not bothered putting in a hardware reset for my SISC microprocessor as it hadn't seemed worth the effort. This was a mistake as the programme counter (pc) is initialised undefined and my Boolean based increment process just didn't work in the simulator. It took me many days to work this out as I could simulate the JUMP instruction as it explicitly set the programme counter value but NOOP just wouldn't increment. It was only after I added the reset instruction when it all started working correctly that I realised what was wrong. The statement after setting the data value is a wait for 150 ns just to leave some time for the simulate to implement the reset.

 

The next instruction I simulated was JUMP 01H

 

01000001

 

which in real hardware would repeatedly jump to itself and therefore loop forever. But as this is a simulator it just applies this machine code instruction for 200 ns. The final machine code instruction applied is NOOP

 

00000000

 

which I left applied for 400 ns.

 

The test bench also generates the clock signal which has a period of 100 ns and this operates for the time given to the instructions above (150 + 200 + 400 = 750 ns) and then it stops.

 

Simulated Test Waveforms

 

It is just about possible to see the waveforms in the screen dump below.

 

 

The programme counter (pc) starts with UU which is undefined, then the C0 value (11000000) is applied which performs a reset on the rising edge of the clock signal, setting PC = 0. This is then followed by the JUMP 01H instruction (41) which sets the programme counter to 01. Next there are four NOOP instructions (00) applied, each one incrementing the programme counter by one.

 

End of Progress

 

Regretfully, this was as far as I managed to progress in implementing my SISC design. The next step would have been to connect the data, PC and connect signals to specific I/O pins on the BASYS 3 board which is called adding the constraints. Then download the completed design to the BASYS 3 board itself and finally to apply logic signals using either the switches provided or some other external source (I was planning to use an Arduino for this).

 

Conclusion

 

I think that my main conclusion is that everything just take so much longer than you expect. Learning something new is always difficult but it would seem that VHDL and Vivado just seemed to be a level significantly more difficult than I was expecting. I have used Xilinx software before and VHDL and FPGAs and without that background knowledge I do not think I would have managed to get as far as I did. The Vivado web pack package is large, complex, not always easy to see what is going on, and slow. It is an industrial package and is aimed at engineers who are working with it full-time. For the beginner and self-learner the learning curve is significant. However, with perseverance and the help of the community, it is possible to start to progress and get to the point of implementing in hardware.

 

I was not able to test the BASYS 3 board in any significant manner as I just couldn't make enough progress with the software. But, from looking at what is there, with built-in 7 segment displays (ideal for the programme counter), switches (suitable for data input), and LEDs for displaying everything logic, it does seem to be a good introductory board. It also has the PMOD connectors which would allow access to many more of the FPGA I/O signals that would also allow it to be used for more advanced designs.

Anonymous
  • It was OK but no where near as good as I had hoped. The software was just so difficult to grasp and it became a bit too much like hard work. Maybe I'll revisit this sometime (or maybe not!).

     

    Dubbie

  • I missed seeing this review last year : ( it's a really nice review. It was also great that it shows the simulation software too.

  • Nice  and honest review - well done.

     

    Vivado is capable of dealing with the world's biggest FPGAs and although Xilinx promote it as an all purpose tool there are times when it feels like using a bulldozer to eat your breakfast cereal.

     

    I have found Altera's free tool to be easier and Lattice's easier still.

     

    For a much more gentle entry into the FPGA world try a Lattice XP2 or Altera MAX10 part.

     

    The free (and paid for) Lattice tools come with a  cut down version of Aldec HDL which I think is a much nicer editor and simulator package then Altera or Xilinx offer.

     

    The Lattice tools are much less capable than A or X but much easier to understand.

     

    I think you need a VHDL tutorial - but if you are still interested please let me know and I'll debug and comment your code.

     

    MK