您的位置:首页 > 编程语言

一个简单CPU VHDL代码解析

2007-04-02 12:45 375 查看
[align=center][/align]
这些天一直在琢磨一个cpu是如何开机reset后运行,完成取指令,译码,计算,存储等操作,还是看一个简单的CPU代码,开始看了MC8051的VHDL代码,不过一头雾水。后来终于在网上找了一个TISC的模拟cpu代码,一共有200多行,不过麻雀虽小,却五脏俱全,而且作者对每行代码都做了详细的说明,下面仔细的分析一下。
先看看作者写的指令说明:
-- Vins VHDL Tisc CPU Core, 15th Nov 2001
-- My first attempt at processor design, thanks to :-
-- Tim Boscke - CPU8BIT2, Rockwell - 6502, Microchip - Pic
-- Jien Chung Lo - EL405 , Steve Scott - Tisc,
-- Giovanni Moretti - Intro to Asm
-- Uses 12 bit program word, and 8 bit data memory

-- 2 reg machine,accumulator based, with akku and index regs
-- Harvard architecture, uses return x so as to eliminate need of pmem
-- indirect instructions like 8051.
-- pc is 10 bits, akku and idx are 8 bit.

-- Has carry and zero flags,
-- three addressing modes:- immediate, indirect and reg.
-- seperate program and data memory for pipelining later...

-- Instructions coded as thus:-

-- Long instructions first - program jump.
-- Both store return address for subroutine calls, 1 deep stack.
-- 0 0 xxxxxxxxxx jc pmem10 ; if c==1, stack = pc, pc <- pmem10, fi
-- 0 1 xxxxxxxxxx jz pmem10 ; if z==1, stack = pc, pc <- pmem10, fi

-- Immediate ops
-- bits 9 and 8 select what to do
-- 1 00 0 xxxxxxxx lda #imm8 ; a= imm8, c=0,
-- 1 00 1 xxxxxxxx ret #imm8 ; a= imm8, pc = stack

-- 1 01 0 xxxxxxxx adc #imm8 ; add with carry imm8, cy and z set
-- 1 01 1 xxxxxxxx adx #imm8 ; add imm8 to idx reg, z=(a==0)

-- Indirect and alu ops
-- bit 9 selects indirect or alu ops

-- Indirect - bits 7 and 8 select what to do
-- 1 10 0 0 xxxxxxx lda [ix] ; load a indirect data mem
-- 1 10 0 1 xxxxxxx sta [ix] ; store a indirect data mem

-- register register
-- 1 10 1 0 xxxxxxx tax ; x = a,
-- 1 10 1 1 xxxxxxx txa ; a = x

-- Arithmetic ops use indirect addressing
-- all alu ops indirect, bits 7 and 8 select alu op.
-- 1 11 00 xxxxxxx add a,[ix] ; add with carry
-- 1 11 01 xxxxxxx sub a,[ix] ; a = a + ~[idx], inc a after for proper subtract
-- 1 11 10 xxxxxxx and a,[ix] ; and mem contents into a
-- 1 11 11 xxxxxxx nor a,[ix] ; nor

-- States.
-- 000 instruction decode
-- 010 load a indirect - lda [ix]
-- 011 stor a indirect - sta [ix]
-- 100 add a,[ix]
-- 101 sub a,[ix[
-- 110 and a,[ix]
-- 111 nor a,[ix]

1.外部接口说明



1.1 处理机控制
Clock --时钟信号 Reset --复位信号
1.2 总线信号
Data[11..0] --数据 address[9..0] --地址
1.3 控制信号
Rd --读信号 wr --写信号 psen --pmem

2.内部信号及寄存器说明
2.1 程序控制
Stack --堆栈 PC --程序计数器
2.2 寄存器
Akku --累加寄存器 Idx --索引寄存器 Z --零标志寄存器
2.3 ALU运算器



运算输入:aluinput,temp

操作 符:aluop
运算结果:aluout
存放目标选择:aludest

3 指令译码及运算
3.1 sub指令译码
-- States.
-- 000 instruction decode
-- 010 load a indirect - lda [ix]
-- 011 stor a indirect - sta [ix]
-- 100 add a,[ix]
-- 101 sub a,[ix]
-- 110 and a,[ix]
-- 111 nor a,[ix]
如对101 sub a,[ix] 汇编指令译码,通过状态-States实现:
(1) 判断状态最高位—>如果为1则进行取数运算à及准备aluop,并设定目标akkuà转换到状态000,指令译码操作。

if( states(2) = '1') then
if( states(1 downto 0) = "01") then --sub add ind
temp <= "0" & (Not data(7 downto 0));
else
temp <= "0" & data(7 downto 0);
end if;

aluop <= states(1 downto 0); -- 00 adc indirect
-- 01 not add ind
-- 10 and indirect
-- 11 nor indirect
-- sort out muxers for alu
aludest <= "00"; -- akku destination
states <= "000"; -- decode
设置好temp,aluop,aludest和states后将要触发(3)alu运算àaluout
然后再进行(2)aludest目标选择运算。
(2)进入alu目标选择

-- Sort out alu ops and source/destination regs
case aludest is
when "00" => -- destination is alu
akku <= aluout(8 downto 0);
when "11" => -- alu destination is idx
idx <= aluout(7 downto 0);
when others => null;
end case; -- end of dest decode
(3)alu运算 两个并发运算过程,确定aluinput及aluout。
-- alu input muxer
with aludest select
aluinput <= ("0" & akku) when "00",
("00" & idx) when "11",
pc when "10",
("0" & temp) when others;

-- Decode and perform arithmetic ops
with aluop select
aluout <= aluinput + ("0" & temp) when "01", -- add
aluinput and ("0" & temp) when "10", -- and
aluinput nor ("0" & temp) when "11", -- nor
not ("0" & temp) when others; -- any use?
(4)由于states <= "000",则下一步进行指令译码工作
case states(1 downto 0) is -- action dependent on states
when "00" => -- instruction decode

-- increment pc first
temp <= "000000001"; -- inc
aludest <= "10"; -- pc and increment and idle
pc <= aluout; -- get result
这里似乎缺少 aluop <= "00" 运算。
主要作用:PC指针加1
(5)地址,数据及读写信号的输出 ---状态变化时触发
-- assign pins
address <= pc when states = "000" else "00"&idx;

data <= ("000" & akku) when states = "011" else "ZZZZZZZZZZZZ";

psen <= '0' when states="000" else '1'; -- pmem read

rd <= '0' when (states(2)='1') or (states="010") else '1'; -- dmem read
wr <= '0' when states="011" else '1'; -- dmem write

4 详细代码清单
-- Vins VHDL Tisc CPU Core, 15th Nov 2001
-- My first attempt at processor design, thanks to :-
-- Tim Boscke - CPU8BIT2, Rockwell - 6502, Microchip - Pic
-- Jien Chung Lo - EL405 , Steve Scott - Tisc,
-- Giovanni Moretti - Intro to Asm
-- Uses 12 bit program word, and 8 bit data memory

-- 2 reg machine,accumulator based, with akku and index regs
-- Harvard architecture, uses return x so as to eliminate need of pmem
-- indirect instructions like 8051.
-- pc is 10 bits, akku and idx are 8 bit.

-- Has carry and zero flags,
-- three addressing modes:- immediate, indirect and reg.
-- seperate program and data memory for pipelining later...

-- Instructions coded as thus:-

-- Long instructions first - program jump.
-- Both store return address for subroutine calls, 1 deep stack.
-- 0 0 xxxxxxxxxx jc pmem10 ; if c==1, stack = pc, pc <- pmem10, fi
-- 0 1 xxxxxxxxxx jz pmem10 ; if z==1, stack = pc, pc <- pmem10, fi

-- Immediate ops
-- bits 9 and 8 select what to do
-- 1 00 0 xxxxxxxx lda #imm8 ; a= imm8, c=0,
-- 1 00 1 xxxxxxxx ret #imm8 ; a= imm8, pc = stack

-- 1 01 0 xxxxxxxx adc #imm8 ; add with carry imm8, cy and z set
-- 1 01 1 xxxxxxxx adx #imm8 ; add imm8 to idx reg, z=(a==0)

-- Indirect and alu ops
-- bit 9 selects indirect or alu ops

-- Indirect - bits 7 and 8 select what to do
-- 1 10 0 0 xxxxxxx lda [ix] ; load a indirect data mem
-- 1 10 0 1 xxxxxxx sta [ix] ; store a indirect data mem

-- register register
-- 1 10 1 0 xxxxxxx tax ; x = a,
-- 1 10 1 1 xxxxxxx txa ; a = x

-- Arithmetic ops use indirect addressing
-- all alu ops indirect, bits 7 and 8 select alu op.
-- 1 11 00 xxxxxxx add a,[ix] ; add with carry
-- 1 11 01 xxxxxxx sub a,[ix] ; a = a + ~[idx], inc a after for proper subtract
-- 1 11 10 xxxxxxx and a,[ix] ; and mem contents into a
-- 1 11 11 xxxxxxx nor a,[ix] ; nor

-- States.
-- 000 instruction decode
-- 010 load a indirect - lda [ix]
-- 011 stor a indirect - sta [ix]
-- 100 add a,[ix]
-- 101 sub a,[ix[
-- 110 and a,[ix]
-- 111 nor a,[ix]

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all; -- has adder built in

entity tisc is
port(
-- bus
-- db only 8 bits wide on dmem side, 12 on pmem side.
data : inout std_logic_vector(11 downto 0);
address : out std_logic_vector(9 downto 0);

-- control - active low
rd : out std_logic; -- dram
wr : out std_logic; -- dram
psen : out std_logic; -- pmem

-- machine control
clock : in std_logic;
reset : in std_logic);
end;

architecture cpu_arch of tisc is
-- Program control
signal stack : std_logic_vector(9 downto 0); -- stack, 1 deep
signal pc : std_logic_vector(9 downto 0); -- program counter

-- Registers
signal akku : std_logic_vector(8 downto 0); -- accumulator, cy is bit 8
signal idx : std_logic_vector(7 downto 0); -- index reg
signal z : std_logic; -- zero flag

-- ALU controls
signal aluout : std_logic_vector(9 downto 0); -- alu result
signal temp : std_logic_vector(8 downto 0); -- temp storage for alu
signal aludest : std_logic_vector(1 downto 0); -- output mux alu
signal aluop : std_logic_vector(1 downto 0); -- alu operation
signal aluinput : std_logic_vector(9 downto 0); -- input to alu

-- machine control
signal states : std_logic_vector(2 downto 0); -- state controller

begin -- start logic decleration

process(clock,reset) -- sequential section
begin
-- check if reset or not
if(reset = '0' ) then -- async reset parameters
-- how do I stop the annoying async error?
pc <= (others => '0');-- reset
states <= (others => '0'); -- decode sub state
aludest <= "01"; -- reset destination dest

elsif rising_edge(clock) then -- actual machine

-- check state machine for indirect alu ops
if( states(2) = '1') then

if( states(1 downto 0) = "01") then --sub add ind
temp <= "0" & (Not data(7 downto 0));
else
temp <= "0" & data(7 downto 0);
end if;

aluop <= states(1 downto 0); -- 00 adc indirect
-- 01 not add ind
-- 10 and indirect
-- 11 nor indirect
-- sort out muxers for alu
aludest <= "00"; -- akku destination
states <= "000"; -- decode

else

-- States decode
case states(1 downto 0) is -- action dependent on states
when "00" => -- instruction decode

-- increment pc first
temp <= "000000001"; -- inc
aludest <= "10"; -- pc and increment and idle
pc <= aluout; -- get result

-- Now decode instr on data bus
-- sort out jz or jc
if( (data(11) = '0') and -- top bit nought, sio a jump...
( ((akku(8) = '1') and (data(10) = '0')) or -- JC
((z='1') and (data(10)='1'))) ) then -- Jz
stack <= pc; --return stack
pc <= data(9 downto 0);
-- states already at decode

else -- top bit is one, so not a jump instruction...
case data(10 downto 9) is
when "01" => -- adc #imm/adx #imm
temp <= "0" & data(7 downto 0);
aluop <= "01"; -- add
aludest <= data(8) & data(8) ; --00 is akku, 11 is idx
-- states already at decode

when "00" => -- lda #imm/ret #imm clear cy
if( data(8) = '1' ) then -- ret ##imm
pc <= stack;
end if;
akku <= "0" & data(7 downto 0);
-- states already at decode

when "10" => -- lda/store or reg/reg
if( data(8) = '1') then -- ld ind /store ind
states <= "01" & data(7);-- "010";
-- reg/reg
elsif( data(7) = '0') then -- tax
idx <= akku(7 downto 0);
else -- txa
akku <= "0" & idx(7 downto 0);
end if;
-- states already at decode

when "11" => -- add ind, and
states <= "1" & data(8 downto 7); -- "100"; -- add/sub ind
-- "110"; -- and/nor ind
when others =>
states <= "000"; -- if non of above, decode/nop
end case; -- end of instruction decode

end if; -- jump if

when "10" => -- lda [ix] - buses should be setup
akku <= "0" & data(7 downto 0);
states <= "000"; -- decode

when "11" => -- sta [ix]
-- akku placed on data bus now
states <= "000"; -- decode

when others =>
states <= "000"; -- decode

end case; -- end of state decode
end if; -- end of alu indirect if
end if; -- rising edge if

-- Sort out alu ops and source/destination regs
case aludest is
when "00" => -- destination is alu
akku <= aluout(8 downto 0);
when "11" => -- alu destination is idx
idx <= aluout(7 downto 0);
when others => null;
end case; -- end of dest decode

end process; -- end sequential section

-- concurrent section
z <= '1' when akku = "000000000" and reset='1' else '0'; -- nice nand here

-- alu input muxer
with aludest select
aluinput <= ("0" & akku) when "00",
("00" & idx) when "11",
pc when "10",
("0" & temp) when others;

-- Decode and perform arithmetic ops
with aluop select
aluout <= aluinput + ("0" & temp) when "01", -- add
aluinput and ("0" & temp) when "10", -- and
aluinput nor ("0" & temp) when "11", -- nor
not ("0" & temp) when others; -- any use?

-- assign pins
address <= pc when states = "000" else "00"&idx;

data <= ("000" & akku) when states = "011" else "ZZZZZZZZZZZZ";

psen <= '0' when states="000" else '1'; -- pmem read

rd <= '0' when (states(2)='1') or (states="010") else '1'; -- dmem read
wr <= '0' when states="011" else '1'; -- dmem write

end cpu_arch;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: