这些天一直在琢磨一个
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;