Обидва способи не гарантують виявлення усіх можливих помилок в результатах проектування. Проте імовірність наявності помилок вони зменшують. Гарантоване виявлення всіх помилок методом верифікації можна досягти лише в спосіб перебору всіх варіантів, що не завжди неможливе. При цьому залишається невідомим метод математичного доведення коректності функціонування машини з програмою, що зберігається в пам’яті.
Пропонуємо наступну тестову програму для тетрадної машини, що додає дворозрядні гексадецимальні числа, виконуючи операцію поцифрово, в два прийоми.
Таблиця 3 – Асемблерна тестова програма і її машинні коди
Адреса Машинний код Асемблер Коментар
000018Load #8
Iніціалізувати регістри числами, що додаються, а саме:
0x29 + 0x48 = 0x71.
Обнулити перенос.
Знайти суму молодших цифр і зберігти в R4.
Додати старші цифри + перенос. Зберігти суму старших цифр в R5.
000130Store R0
000214Load #4
000331Store R1
000419Load #9
000532Store R2
000612Load #2
000733Store R3
000800Clear C
000940Load R0
000a52Add R2
000b34Store R4
000c41Load R1
000d53Add R3
000e35Store R5
Loop:
000f44Load R4Показати мол. цифру.
Показати старшу цифру.
Показувати безперервно.
001045Load R5
00118fJump #loop
Ясна річ, машинні коди тестової програми мають фіксуватися в VHDL моделі пам’яті програм. Зауважимо, що для виконуваних пар інструкцій з кодами 0х53/0x35 та 0x44/0x8F часовим симулюванням отримано і проаналізовано подані нижче часові діаграми виконання тестової програми.
Розробка VHDL моделі процесора
Виконаємо розробку VHDL моделі процесора на основі прототипної моделі процесора “Gnome”, що подана роботою [1] мовою ABEL. На відміну від прототипа ми задамо цільову ПЛІС Віртекс-2 з вбудованими елементами RAM і ROM. При цьому об’єднаємо CPU, RAM і ROM до єдиного комп’ютера внутрішньо кристальними шинами, одна з яких є двонаправленою. Розглянемо VHDL текст моделі.
-- GNOME micro processor unit
LIBRARY IEEE;
USE IEEE.std_logic_1164.all;
USE IEEE.std_logic_unsigned.all;
-- interface
ENTITY gnome IS
PORT (
clk: IN STD_LOGIC;-- clock
reset: IN STD_LOGIC;-- reset control input
address: OUT STD_LOGIC_VECTOR (6 DOWNTO 0); -- external RAM address
data: INOUT STD_LOGIC_VECTOR (7 DOWNTO 0); -- system bus
web: OUT STD_LOGIC; -- external RAM active-low write-enable
oeb: OUT STD_LOGIC; -- external RAM active-low output-enable
sel_ram: out std_logic;
carry_out : out std_logic;
pc_out: OUT STD_LOGIC_VECTOR (6 DOWNTO 0);
ir_out: out std_logic_vector (7 downto 0);
acc_out: out std_logic_vector (3 downto 0));
END gnome;
-- GNOME processor architecture behavioral description
ARCHITECTURE gnome_arch OF gnome IS
-- current and next GNOME state-machine state
SIGNAL curr_st, next_st: STD_LOGIC_VECTOR (1 DOWNTO 0);
-- possible GNOME state-machine states and their definitions
CONSTANT FETCH: STD_LOGIC_VECTOR (1 DOWNTO 0) := "00"; -- fetch instruction
CONSTANT DECODE: STD_LOGIC_VECTOR (1 DOWNTO 0) := "01"; -- decode
CONSTANT EXECUTE: STD_LOGIC_VECTOR (1 DOWNTO 0) := "11"; -- execute
-- current and next program counter value
SIGNAL curr_pc, next_pc: STD_LOGIC_VECTOR (6 DOWNTO 0);
-- current and next accumulator value
SIGNAL curr_acc, next_acc: STD_LOGIC_VECTOR (3 DOWNTO 0);
-- current and next carry flag value
SIGNAL curr_carry, next_carry: STD_LOGIC;
-- current and next zero flag value
SIGNAL curr_zero, next_zero: STD_LOGIC;
-- sum vector for holding adder output
SIGNAL sum: STD_LOGIC_VECTOR (4 DOWNTO 0);
SIGNAL read: STD_LOGIC; -- 1 when reading RAM
SIGNAL write: STD_LOGIC; -- 1 when writing RAM
SIGNAL sel_data_ram: STD_LOGIC;-- 1 when accessing R0-R15
SIGNAL jump_pc: STD_LOGIC; -- 1 when overwriting PC
SIGNAL inc_pc: STD_LOGIC; -- 1 when incrementing PC
SIGNAL ld_ir: STD_LOGIC; -- 1 when loading IR
SIGNAL ld_ir_lsn: STD_LOGIC;-- 1 when loading LSN of IR
-- ALU operation code possible ALU opcodes
SIGNAL alu_op: STD_LOGIC_VECTOR (2 DOWNTO 0);
--pass input to output
CONSTANT PASS_OP: STD_LOGIC_VECTOR (2 DOWNTO 0):= "001";
CONSTANT ADD_OP: STD_LOGIC_VECTOR (2 DOWNTO 0):= "010"; -- add inputs
CONSTANT XOR_OP: STD_LOGIC_VECTOR (2 DOWNTO 0):= "011"; -- XOR inputs
CONSTANT AND_OP: STD_LOGIC_VECTOR (2 DOWNTO 0):= "100"; -- test input for 0
CONSTANT SET_CARRY_OP: STD_LOGIC_VECTOR (2 DOWNTO 0) := "101"; --set carry
CONSTANT CLR_CARRY_OP: STD_LOGIC_VECTOR (2 DOWNTO 0) := "110"; --clear carry
-- current and next instruction register
SIGNAL curr_ir, next_ir: STD_LOGIC_VECTOR (7 DOWNTO 0);
-- possible instruction opcodes
CONSTANT CLEAR_C: STD_LOGIC_VECTOR (7 DOWNTO 0) := "00000000";
CONSTANT SET_C: STD_LOGIC_VECTOR (7 DOWNTO 0) := "00000001";
CONSTANT SKIP_C: STD_LOGIC_VECTOR (7 DOWNTO 0) := "00000010";
CONSTANT SKIP_Z: STD_LOGIC_VECTOR (7 DOWNTO 0) := "00000011";
CONSTANT LOAD_IMM: STD_LOGIC_VECTOR (3 DOWNTO 0) := "0001";
CONSTANT ADD_IMM: STD_LOGIC_VECTOR (3 DOWNTO 0) := "0010";
CONSTANT STORE_DIR:STD_LOGIC_VECTOR (3 DOWNTO 0) := "0011";
CONSTANT LOAD_DIR: STD_LOGIC_VECTOR (3 DOWNTO 0) := "0100";
CONSTANT ADD_DIR: STD_LOGIC_VECTOR (3 DOWNTO 0) := "0101";
CONSTANT XOR_DIR: STD_LOGIC_VECTOR (3 DOWNTO 0) := "0110";
CONSTANT TEST_DIR: STD_LOGIC_VECTOR (3 DOWNTO 0) := "0111";
CONSTANT JUMP: STD_LOGIC := '1';
BEGIN
carry_out <= curr_carry;
pc_out <= curr_pc;
acc_out <= curr_acc;
ir_out <= curr_ir;
sel_ram <= sel_data_ram;
-- external RAM control signals
oeb <= NOT(read);-- enable RAM drivers during RAM read operations
web <= NOT(write);-- RAM write line (in last half of clock)
-- address either the data or program sections of the external RAM
address <= "000" & curr_ir(3 DOWNTO 0) WHEN sel_data_ram='1' ELSE
curr_pc;
-- drive the accumulator contents into the RAM during write operations
-- but disable the drivers into high-impedance state at all other times
data <= "0000" & curr_acc WHEN write='1' ELSE "ZZZZZZZZ";
-- load the instruction register with a new opcode
next_ir <= data WHEN ld_ir='1' ELSE
-- or load only the lower 4 bits of the IR with data
curr_ir(7 DOWNTO 4) & data(3 DOWNTO 0) WHEN ld_ir_lsn='1' ELSE
-- or else don't change the IR
curr_ir;
-- load the PC with an address to jump to
next_pc <= curr_ir(6 DOWNTO 0) WHEN jump_pc='1' ELSE
-- or increment the PC
curr_pc+1 WHEN inc_pc='1' ELSE
-- or else don't change the PC
curr_pc;
-- this process describes the operations of the ALU
PROCESS (alu_op,curr_zero,curr_carry,curr_acc,curr_ir,sum)
BEGIN
-- set the default values for these signals to avoid synthesis of
-- implied latches
sum <= "00000";
next_acc <= "0000";
next_carry <= '0';
next_zero <= '0';
CASE alu_op IS
WHEN ADD_OP =>
-- add the accumulator with the lower 4 bits of the IR and the carry
sum <= ('0' & curr_acc) + ('0' & curr_ir(3 DOWNTO 0))
+ ("0000" & curr_carry);
next_acc <= sum(3 DOWNTO 0);-- ACC gets lower 4 bits of the sum
next_carry <= sum(4);-- carry is the most significant bit of the sum
next_zero <= curr_zero;-- zero flag is not changed
WHEN XOR_OP =>
-- XOR the accumulator with the lower 4 bits of the IR
next_acc <= curr_acc XOR curr_ir(3 DOWNTO 0);
next_carry <= curr_carry; -- carry flag is not changed
next_zero <= curr_zero; -- zero flag is not changed
WHEN PASS_OP =>
-- pass lower 4 bits of IR into ACC
next_acc <= curr_ir(3 DOWNTO 0);
next_carry <= curr_carry; -- carry flag is not changed
next_zero <= curr_zero; -- zero flag is not changed
WHEN AND_OP =>
-- test the ACC for zeroes in unmasked bit positions
next_acc <= curr_acc; -- ACC is not changed
next_carry <= curr_carry;-- carry is not changed
-- zero flag is set if ACC has zeroes where IR has ones
next_zero <= NOT( (curr_acc(3) AND curr_ir(3))
OR (curr_acc(2) AND curr_ir(2))
OR (curr_acc(1) AND curr_ir(1))
OR (curr_acc(0) AND curr_ir(0)));
WHEN SET_CARRY_OP =>
-- set the carry bit
next_acc <= curr_acc;-- ACC is not changed
next_carry <= '1';
next_zero <= curr_zero;-- zero flag is not changed
WHEN CLR_CARRY_OP =>
-- clear the carry bit
next_acc <= curr_acc;-- ACC is not changed
next_carry <= '0';
next_zero <= curr_zero;-- zero flag is not changed
WHEN OTHERS =>
-- don't do anything for undefined ALU opcodes
next_acc <= curr_acc;
next_carry <= curr_carry;
next_zero <= curr_zero;
END CASE;
END PROCESS;
-- this process describes the transitions of the GNOME state machine
-- and sets the control signals that are activated in each state
PROCESS(curr_st,curr_carry,curr_zero,curr_ir)
BEGIN
-- set the default values for these signals to avoid synthesis
-- of implied latches
sel_data_ram <= '0';
read <= '0';
write <= '0';
ld_ir <= '0';
ld_ir_lsn <= '0';
inc_pc <= '0';
jump_pc <= '0';
alu_op <= "000";
CASE curr_st IS
WHEN FETCH =>
-- fetch an instruction from external RAM
sel_data_ram <= '0'; -- select the instruction RAM
read <= '1'; -- read from the RAM
ld_ir <= '1';-- load the instruction
-- register with the opcode
inc_pc <= '1'; -- increment the PC
-- to the next instruction
next_st <= DECODE; -- then decode the
-- instruction that was just loaded
WHEN DECODE =>
-- decode the instruction. Actually, this state is used to
-- read a direct-address operand from the data section of the
-- external RAM and store it in the lower 4 bits of the IR.
IF curr_ir(7 DOWNTO 4)=LOAD_DIR THEN
sel_data_ram <= '1';
read <= '1';
ld_ir_lsn <= '1';
END IF;
IF curr_ir(7 DOWNTO 4)=ADD_DIR THEN
sel_data_ram <= '1';
read <= '1';
ld_ir_lsn <= '1';
END IF;
IF curr_ir(7 DOWNTO 4)=XOR_DIR THEN
sel_data_ram <= '1';
read <= '1';
ld_ir_lsn <= '1';
END IF;
IF curr_ir(7 DOWNTO 4)=TEST_DIR THEN
sel_data_ram <= '1';
read <= '1';
ld_ir_lsn <= '1';
END IF;
next_st <= EXECUTE;-- then execute the instruction
WHEN EXECUTE =>
-- execute the instruction.
IF curr_ir=CLEAR_C THEN
alu_op <= CLR_CARRY_OP;-- clear the carry flag
END IF;
IF curr_ir=SET_C THEN
alu_op <= SET_CARRY_OP;-- set the carry flag
END IF;
IF curr_ir=SKIP_C THEN-- skip the next instruction
inc_pc <= curr_carry; -- if the carry flag is set
END IF;
IF curr_ir=SKIP_Z THEN-- skip the next instruction
inc_pc <= curr_zero; -- if the zero flag is set
END IF;
IF curr_ir(7 DOWNTO 4)=LOAD_IMM THEN
-- load the ACC with immediate
alu_op <= PASS_OP;
-- data from the lower 4 bits of IR
END IF;
IF curr_ir(7 DOWNTO 4)=ADD_IMM THEN
-- add the lower 4 bits of the
alu_op <= ADD_OP;-- IR to the ACC
END IF;
IF curr_ir(7)=JUMP THEN
-- jump to the address stored in the
jump_pc <= '1';
-- lower 7 bits of the IR
END IF;
IF curr_ir(7 DOWNTO 4)=STORE_DIR THEN
-- write the ACC to RAM
sel_data_ram <= '1';
write <= '1';
END IF;
IF curr_ir(7 DOWNTO 4)=LOAD_DIR THEN
-- load the ACC with the
alu_op <= PASS_OP;
-- data read from RAM in the previous cycle
END IF;
IF curr_ir(7 DOWNTO 4)=ADD_DIR THEN
-- add the data read from RAM in
alu_op <= ADD_OP;
-- the previous cycle to the ACC
END IF;
IF curr_ir(7 DOWNTO 4)=XOR_DIR THEN
-- XOR the data read from RAM in
alu_op <= XOR_OP;
-- the previous cycle to the ACC
END IF;
IF curr_ir(7 DOWNTO 4)=TEST_DIR THEN
-- mask the ACC with the value
alu_op <= AND_OP;
-- read from RAM in the previous cycle and
END IF;
-- set the zero flag if all the bits are zero
next_st <= FETCH;
-- execution complete, so go fetch another instruction
WHEN others =>
END CASE;
END PROCESS;
-- this process makes the next value of all these signals into the
-- current value on the rising clock edge
PROCESS (clk,reset)
BEGIN
-- asynchronously reset the state of the GNOME microcomputer
IF reset='1' THEN
curr_st <= FETCH;-- start by fetching instructions
curr_pc <= "0000000";-- start at beginning of instruction RAM
curr_ir <= "00000000";
curr_acc <= "0000";-- clear accumulator
curr_carry <= '0'; -- clear carry flag
curr_zero <= '0'; -- clear zero flag
-- otherwise, update state on the rising clock edge
ELSIF (clk'event AND clk='1') THEN
curr_st <= next_st;
curr_pc <= next_pc;
curr_ir <= next_ir;
curr_acc <= next_acc;
curr_carry <= next_carry;
curr_zero <= next_zero;
END IF;
END PROCESS;
END gnome_arch;
В відповідному розділі записки потрібно подати:
-текст моделі з коментарями державною мовою;
-побудовану на основі VHDL моделі структурну схему процесора (це можна зробити за допомогою утиліти RTL schematic Viewer, що містить САПР WebPack);
-додаткові (поза коментарями) роз’яснення щодо семантики VHDL моделі;
-витяги з протоколів синтезу, імплементування, програмування, діаграму часового симулювання поведінки процесора як окремого проекту.
Розробка VHDL моделі пам’яті даних
Модель пам’яті даних складено так, аби VHDL синтезатор за стилем написання винайшов, що її треба реалізувати на вбудованих до цільової ПЛІС елементах RAM. Це покращує реалізацію проекту. Існує варіант прямого запису в тексті моделі бібліотечних посилань на вбудовані елементи. Проте така модель набуває рис непересувної, хоча і ефективної моделі низького рівня. Це не завжди є бажаним. Подамо текст моделі пам’яті даних.