FONT SIZE : AAA
In order to simplify the VHDL for each of the individual blocks, a set of standard functions have been defined in a package called processor_functions. This is used to defined useful types and functions for this set of models. The VHDL for the package is given below:
1 library ieee;
2 use ieee.std_logic_1164.all; 34 package processor_functions is
5 type opcode is (load, store, add, not, and, or, xor, inc, sub, branch );
6 function decode ( word : std_logic_vector ) return opcode;
7 constant n : integer := 16;
8 constant oplen : integer := 4;
9 type memory_array is array ( 0 to 2∗∗(n−oplen−1) of
10 std_logic_vector(n−1 downto 0);
11 constant reg_zero : unsigned (n−1 downto 0) :=
12 (others => 0);
13 end package processor_functions;
14
15 package body processor_functions is
16 function decode (word : std_logic_vector) return opcode is
17 variable opcode_out : opcode;
18 begin
19 case word(n−1 downto n−oplen−1) is
20 when 0000 => opcode_out := load;
21 when 0001 => opcode_out := store;
22 when 0010 => opcode_out := add;
23 when 0011 => opcode_out := not;
24 when 0100 => opcode_out := and;
25 when 0101 => opcode_out := or;
26 when 0110 => opcode_out := xor;
27 when 0111 => opcode_out := inc;
28 when 1000 => opcode_out := sub;
29 when 1001 => opcode_out := branch;
30 when others => null;
31 end case;
32 return opcode_out;
33 end function decode;
34 end package body processor_functions;
The program counter (PC) needs to have the system clock and reset connections, and the system bus (defined as inout so as to be readable and writable by the PC register block). In addition, there are several control signals required for correct operation. The first is the signal to increment the PC (PC_inc), the second is the control signal to load the PC with a specified value (PC_load) and the final is the signal to make the register contents visible on the internal bus (PC_valid). This signal ensures that the value of the PC register will appear to be high impedance (Z) when the register is not required on the processor bus. The system bus (PC_bus) is defined as a std_logic_vector, with direction inout to ensure the ability to read and write. The resulting VHDL entity is given here:
library ieee;
2 use ieee.std_logic_1164.all; 3 entity pc is
4 port (
5 clk : in std_logic;
6 nrst : in std_logic;
7 pc_inc : in std_logic;
8 pc_load : in std_logic;
9 pc_valid : in std_logic;
10 pc_bus : inout std_logic_vector(n−1 downto 0)
11 );
12 end entity pc;
The architecture for the program counter must handle all of the various configurations of the program counter control signals and also the communication of the data into and from the internal bus correctly. The PC model has an asynchronous part and a synchronous section. If the PC_valid goes low at any time, the value of the PC_bus signal should be set to Z across all of its bits. Also, if the reset signal goes low, then the PC should reset to zero.
The synchronous part of the model is the increment and load functionality. When the clk rising edge occurs, then the two signals PC_load and PC_inc are used to define the function of the counter. The precedence is that if the increment function is high, then regardless of the load function, the counter will increment. If the increment function (PC_inc) is low, then the PC will load the current value on the bus, if and only if the PC_load signal is also high. The resulting VHDL is given as:
1 architecture rtl of pc is
2 signal counter : unsigned (n−1 downto 0);
3 begin
4 pc_bus <= std_logic_vector(counter)
5 when pc_valid = 1 else (others => z);
6 process (clk, nrst) is
7 begin
8 if nrst = 0 then
9 count <= 0;
10 elsif rising_edge(clk) then
11 if pc_inc = 1 then
12 count <= count + 1;
13 else
14 if pc_load = 1 then
15 count <= unsigned(pc_bus);
16 end if;
17 end if;
18 end if;
19 end process;
20 end architecture rtl;
The instruction register (IR) has the same clock and reset signals as the PC, and also the same interface to the bus (IR_bus) defined as a std_logic_vector of type INOUT. The IR also has two further control signals, the first being the command to load the instruction register (IR_load), and the second being to load the required address onto the system bus (IR_address). The final connection is the decoded opcode that is to be sent to the system controller. This is defined as a simple unsigned integer value with the same size as the basic system bus. The basic VHDL for the entity of the IR is given as follows:
1 library ieee;
2 use ieee.std_logic_1164.all; 3 use work.processor_functions.all; 4 entity ir is
5 port ( 6 clk : in std_logic;
7 nrst : in std_logic;
8 ir_load : in std_logic;
9 ir_valid : in std_logic;
10 ir_address : in std_logic;
11 ir_opcode : out opcode;
12 ir_bus : inout std_logic_vector(n−1 downto 0)
13 );
14 end entity ir;
The function of the IR is to decode the opcode in binary form and then pass to the control block. If the IR_valid is low, the the bus value should be set to Z for all bits. If the reset signal (nsrt) is low, then the register value internally should be set to all 0s.
On the rising edge of the clock, the value on the bus shall be sent to the internal register and the output opcode shall be decoded asynchronously when the value in the IR changes. The resulting VHDL architecture is given here:
1 architecture rtl of ir is
23 signal ir_internal : std_logic_vector (n−1 downto 0);
4 begin
5 ir_bus <= ir_internal
6 when ir_valid = 1 else (others => z);
7 ir_opcode <= decode(ir_internal);
8 process (clk, nrst) is
9 begin
10 if nrst = 0 then
11 ir_internal <= (others => 0);
12 elsif rising_edge(clk) then
13 if ir_load = 1 then
14 ir_internal <= ir_bus;
15 end if;
16 end if;
17 end process;
18 end architecture rtl;
In this VHDL, notice that we have used the predefined function Decode from the processor_functions package previously defined. This will look at the top 4 bits of the address given to the IR and decode the relevant opcode for passing to the controller.
The Arithmetic and Logic Unit (ALU) has the same clock and reset signals as the PC, and also the same interface to the bus (ALU_bus) defined as a std_logic_vector of type INOUT. The ALU also has three further control signals, which can be decoded to map to the eight individual functions required of the ALU. The ALU also contains the Accumulator (ACC) which is a std_logic_vector of the size defined for the system bus width. There is also a single bit output ALU_zero which goes high when all the bits in the accumulator are zero. The basic VHDL for the entity of the ALU is given as follows:
1 library ieee;
2 use ieee.std_logic_1164.all; 3 use work.processor_functions.all; 4 entity alu is
5 port ( 6 clk : in std_logic;
7 nrst : in std_logic;
8 alu_cmd : in std_logic_vector(2 downto 0) ;
9 alu_zero : out std_logic;
10 alu_valid : in std_logic;
11 alu_bus : inout std_logic_vector(n−1 downto 0)
12 );
13 end entity alu;
The function of the ALU is to decode the ALU_cmd in binary form and then carry out the relevant function on the data on the bus, and the current data in the accumulator. If the ALU_valid is low, then the bus value should be set to Z for all bits. If the reset signal (nsrt) is low, then the register value internally should be set to all 0s. On the rising edge of the clock, the value on the bus shall be sent to the internal register and the command shall be decoded. The resulting VHDL architecture is given here:
1 architecture rtl of alu is
2 signal acc : std_logic_vector (n−1 downto 0);
3 begin
4 alu_bus <= acc
5 when acc_valid = 1 else (others => z);
6 alu_zero <= 1 when acc = reg_zero else 0;
7 process (clk, nrst) is
8 begin
9 if nrst = 0 then
10 acc <= (others => 0);
11 elsif rising_edge(clk) then
12 case acc_cmd is
13 −− load the bus value into the accumulator
14 when 000 => acc <= alu_bus;
15 −− add the acc to the bus value
16 when 001 => acc <= add(acc,alu_bus);
17 −− not the bus value
18 when 010 => acc <= not alu_bus;
19 −− or the acc to the bus value
20 when 011 => acc <= acc or alu_bus;
21 −− and the acc to the bus value
22 when 100 => acc <= acc and alu_bus;
23 −− xor the acc to the bus value
24 when 101 => acc <= acc xor alu_bus;
25 −− increment acc
26 when 110 => acc <= acc + 1;
27 −− store the acc value
28 when 111 => alu_bus <= acc;
29 end if;
30 end process;
31 end architecture rtl;
The processor requires a RAM memory, with an address register (MAR) and a data register (MDR). There therefore needs to be a load signal for each of these registers: MDR_load and MAR_load. As it is a memory, there also needs to be an enable signal (M_en), and also a signal to denote Read or Write modes (M_rw). Finally, the connection to the system bus is a standard inout vector as has been defined for the other registers in the microprocessor.
The basic VHDL for the entity of the memory block is given here:
1 library ieee;
2 use ieee.std_logic_1164.all; 3 use work.processor_functions.all; 4 entity memory is
5 port ( 6 clk : in std_logic;
7 nrst : in std_logic;
8 mdr_load : in std_logic;
9 mar_load : in std_logic;
10 mar_valid : in std_logic;
11 m_en : in std_logic;
12 m_rw : in std_logic;
13 mem_bus : inout std_logic_vector(n−1 downto 0)
14 );
15 end entity memory;
The memory block has three aspects. The first is the function in which the memory address is loaded into the memory address register (MAR). The second function is either reading from or writing to the memory using the memory data register (MDR). The final function, or aspect, of the memory is to store the actual program that the processor will run. In the VHDL model, we will achieve this by using a constant array to store the program values.
The resulting basic VHDL architecture is given as follows:
12 architecture rtl of memory is
3 signal mdr : std_logic_vector(wordlen−1 downto 0);
4 signal mar : unsigned(wordlen−oplen−1 downto 0);
5 begin
6 mem_bus <= mdr
7 when mem_valid = 1 else (others => z);
8 process (clk, nrst) is
9 variable contents : memory_array;
10 constant program : contents :=
11 (
12 0 => 0000000000000011,
13 1 => 0010000000000100,
14 2 => 0001000000000101,
15 3 => 0000000000001100,
16 4 => 0000000000000011,
17 5 => 0000000000000000 ,
18 others => (others => 0)
19 );
20 begin
21 if nrst = 0 then
22 mdr <= (others => 0);
23 mdr <= (others => 0);
24 contents := program;
25 elsif rising_edge(clk) then
26 if mar_load = 1 then
27 mar <= unsigned(mem_bus(n−oplen−1 downto 0));
28 elsif mdr_load = 1 then
29 mdr <= mem_bus;
30 elsif mem_en = 1 then
31 if mem_rw = 0 then
32 mdr <= contents(to_integer(mar));
33 else
34 mem(to_integer(mar)) := mdr;
35 end if;
36 end if;
37 end if;
38 end process;
39 end architecture rtl;
We can look at some of the VHDL in a bit more detail and explain what is going on at this stage. There are two internal signals to the block, mdr and mar (the data and address, respectively). The first aspect to notice is that we have defined the MAR as an unsigned rather than as a std_logic_vector. We have done this to make indexing direct. The MDR remains as a std_logic_vector. We can use an integer directly, but an unsigned translates easily into a std_logic_vector.
1 signal mdr : std_logic_vector(wordlen−1 downto 0);
2 signal mar : unsigned(wordlen−oplen−1 downto 0);
The second aspect is to look at the actual program itself. We clearly have the possibility of a large array of addresses, but in this case we are defining a simple three line program:
1 c=a+b
The binary code is shown below:
1 0 => 0000000000000011
2 1 => 0010000000000100
3 2 => 0001000000000101
4 3 => 0000000000001100
5 4 => 0000000000000011
6 5 => 0000000000000000
7 Others => (others => 0)
For example, consider the line of the declared value for address 0. The 16 bits are defined as 0000000000000011. If we split this into the opcode and data parts we get the following:
1 Opcode 0000
2 Data 000000000011
In other words, this means LOAD the variable from address 3. Similarly, the second line is ADD from 4, finally the third command is STORE in 5. In addresses 3, 4, and 5, the three data variables are stored.
The operation of the processor is controlled in detail by the sequencer, or controller block. The function of this part of the processor is to take the current program counter address, look up the relevant instruction from memory, move the data around as required, setting up all the relevant control signals at the right time, with the right values. As a result, the controller must have the clock and reset signals (as for the other blocks in the design), a connection to the global bus, and finally all the relevant control signals must be output. An example entity of a controller is given here:
1 library ieee;
2 use ieee.std_logic_1164.all; 3 use work.processor_functions.all; 4 entity controller is
5 generic ( 6 n : integer := 16
7 );
8 port ( 9 clk : in std_logic;
10 nrst : in std_logic;
11 ir_load : out std_logic;
12 ir_valid : out std_logic;
13 ir_address : out std_logic;
14 pc_inc : out std_logic;
15 pc_load : out std_logic;
16 pc_valid : out std_logic;
17 mdr_load : out std_logic;
18 mar_load : out std_logic;
19 mar_valid : out std_logic;
20 m_en : out std_logic;
21 m_rw : out std_logic;
22 alu_cmd : out std_logic_vector(2 downto 0);
23 control_bus : inout std_logic_vector(n−1 downto 0)
24 );
25 end entity controller;
Using this entity, the control signals for each separate block are then defined, and these can be used to carry out the functionality requested by the program. The architecture for the controller is then defined as a basic state machine to drive the correct signals. The basic state machine for the processor is defined in Figure 8.4.
We can implement this using a basic VHDL architecture that implements each state using a new state type and a case statement to manage the flow of the state machine. The basic VHDL architecture follows and it includes the basic synchronous machine control section (reset and clock) and the management of the next stage logic.
1 architecture rtl of controller is
2 type states is (s0,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10);
3 signal current_state, next_state : states;
4 begin
5 state_sequence: process (clk, nrst) is
6 if nrst = 0 then
7 current_state <= s0;
8 else
9 if rising_edge(clk) then
10 current_state <= next_state;
11 end if;
12 end if;
13 end process state_sequence;
14
15 state_machine : process ( present_state, opcode ) is
16 −− state machine goes here
17 end process state_machine;
18 end architecture;
You can see from this VHDL that the first process (state_sequence) manages the transition of the current_state to the next_state and also the reset condition. Notice that this is a synchronous machine and as such waits for the rising_edge of the clock, and that the reset is asynchronous. The second process (state_machine) waits for a change in the state or the opcode and this is used to manage the transition to the next state, although the actual transition itself is managed by the state_sequence process. This process is given in the VHDL here:
1 state_machine : process ( present_state, opcode ) is
2 begin
3 −− reset all the control signals
4 ir_load <= 0 ;
5 ir_valid <= 0 ;
6 ir_address <= 0 ;
7 pc_inc <= 0 ;
8 pc_load <= 0 ;
9 pc_valid <= 0 ;
10 mdr_load <= 0 ;
11 mar_load <= 0 ;
12 mar_valid <= 0 ;
13 m_en <= 0 ;
14 m_rw <= 0 ;
15 case current_state is
16 when s0 =>
17 pc_valid<= 1 ;
18 mar_load<= 1 ;
19 pc_inc<= 1 ;
20 pc_load<= 1 ;
21 next_state<=s1;
22 when s1 =>
23 m_en<= 1 ;
24 m_rw<= 1 ;
25 next_state<=s2;
26 when s2 =>
27 mdr_valid<= 1 ;
28 ir_load<= 1 ;
29 next_state<=s3;
30 when s3 =>
31 mar_load<= 1 ;
32 ir_address<= 1 ;
33 if opcode = store then
34 next_state<=s4;
35 else
36 next_state <=s6;
37 end if;
38 when s4 =>
39 mdr_load<= 1 ;
40 acc_valid<= 1 ;
41 next_state<=s5;
42 when s5 =>
43 m_en <= 1 ;
44 next_state <= s0;
45 when s6 =>
46 m_en<= 1 ; m_rw<= 1 ;
47 if opcode = load then
48 next_state<=s7;
49 else
50 next_state <=s8;
51 end if;
52 when s7 =>
53 mdr_valid<= 1 ;
54 acc_load<= 1 ;
55 next_state<=s0;
56 when s8 =>
57 m_en<= 1 ;
58 m_rw<= 1 ;
59 if opcode = add then
60 next_state<=s9;
61 else
62 next_state <=s10;
63 end if;
64 when s9 =>
65 alu_add <= 1 ;
66 next_state<=s0;
67 when s10 =>
68 alu_sub <= 1 ;
69 next_state<=s0;
70 end case;
71 end process state_machine;
Now that the important elements of the processor have been defined, it is a simple matter to instantiate them in a basic VHDL netlist and create a microprocessor using these building blocks. It is also a simple matter to modify the functionality of the processor by changing the address/data bus widths or extend the instruction set.
Manufacturer:Xilinx
Product Categories: FPGAs (Field Programmable Gate Array)
Lifecycle:Obsolete -
RoHS:
Manufacturer:Xilinx
Product Categories: FPGAs (Field Programmable Gate Array)
Lifecycle:Obsolete -
RoHS:
Manufacturer:Xilinx
Product Categories:
Lifecycle:Obsolete -
RoHS: No RoHS
Manufacturer:Xilinx
Product Categories:
Lifecycle:Any -
RoHS: -
Manufacturer:Xilinx
Product Categories: CPLDs
Lifecycle:Active Active
RoHS: No RoHS
Support