一个简单CPU VHDL代码解析

来源:互联网 发布:淘宝返现是真的吗 编辑:程序博客网 时间:2024/06/05 05:29
这些天一直在琢磨一个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;
  
原创粉丝点击