在线咨询
eetop公众号 创芯大讲堂 创芯人才网
切换到宽版

EETOP 创芯网论坛 (原名:电子顶级开发网)

手机号码,快捷登录

手机号码,快捷登录

找回密码

  登录   注册  

快捷导航
搜帖子
查看: 9761|回复: 33

开源软核学习笔记04(从10天实现处理器—OpenMIPS开发笔记找思路)——2014_1_19

[复制链接]
发表于 2014-1-19 08:45:48 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?注册

x
按照笔记03计划,04应该从上而下或从形而上学习MC8051软核,但是这里却是OpenMips。
其实从笔记02到03,也不是很连贯的,原因就在于学习的思路很难保证是线性的。

这次选择OpenMips(http://bbs.eetop.cn/thread-426187-1-1.html)来学习,有几个原因:
1、自己一开始就打算学习OpenMIPS,不过能力不足。经过了半个月相关学习,增强了些基础;
2、直接省略了里面的汇编和Ubuntu的内容,直接使用生成好的rom文件,这样可以避免Linux系统;
3、一直没找到8051最小系统软核(类似《编码》一书那样的系统),因为这样的系统好理解;而OpenMIPS是从这样的一个小系统起步的;
4、OpenMIPS第一天第二天内容是我的关注点,暂时不关心更多的命令和更复杂的功能,而是要在OpenMIPS最小系统上再细分,即从上向下学习低层。

*********************************************************

首先,找到OpenMIPS_VHDL_study_v1.0\10_Days_make_OpenMIPS\Day2 目录,在该目录下建新目录modelim,然后把inst_rom.data 文件复制到该目录下。然后打开modelsim,新项目选择新建的modelsim目录。

然后按照教程作就可以了。

在理解当中,最困扰我的是那五个寄存器,我根据上下文和标示暂时区分了一下寄存器。

01.JPG

02.JPG

03.JPG

04.JPG

05.JPG

06.JPG

07.JPG

08.JPG

09.JPG

10.JPG

11.JPG

12.JPG

13.JPG

下一步就是理解上面的模块是怎样组织的?还有为什么?
 楼主| 发表于 2014-1-20 09:03:36 | 显示全部楼层
************************
先分析imem文件,同时学习VHDL语言。
另外DMEM文件暂时可以被去掉(减少工作量),同时各文件的相应内容会被去除。

************************

imem文件各部分
14.JPG

各部分的说明(VHDL学习)
15.JPG

*************************

实体部分:
entity imem is
  port (
    rst   : in  std_logic;
      addr  : in  word;
      data  : out word
  );
end;

16.JPG

17.JPG


**********************************

结构体说明部分:
architecture rtl of imem is

  --此处的IMEMSIZEINWORD是在stdlib.vhd中定义的imem的大小(单位是word)
    TYPE mem is ARRAY (IMEMSIZEINWORD downto 0) of std_logic_vector(31 downto 0);
    signal mem0: mem;
   
    --使用inst_rom.data文件初始化imem
    file f1:text is in "inst_rom.data";

这里需要理解的知识点有,

18.JPG

这里用到了数组
TYPE mem is ARRAY (IMEMSIZEINWORD downto 0) of std_logic_vector(31 downto 0);
19.JPG

其中,
STD_LOGIC_VECTOR类型定义如下:
    TYPE  STD_LOGIC_VECTOR IS  ARRAY (NATURAL  
                            RANGE <>)  OF  STD_LOGIC;

还用到了文件
20.JPG

**********************************************

结构体逻辑说明部分;

21.JPG

*********************************

    [进程名:]   PROCESS  (敏感信号表)
                        进程说明语句

        process(rst, addr)
                variable li : line;
                variable k,i : integer;
                variable j : character;
                variable good : boolean;
                variable temp : std_logic_vector(31 downto 0);
                variable temp1: integer range 0 to IMEMSIZEINWORD;

22.JPG

涉及到变量
23.JPG

变量是一个局部量 ,它只用于进程和子程序。变量必须在进程或子程序的说明区域中加以说明。

变量赋值是直接的、非预设的 ,它在某一时刻仅包含一个值。变量的赋值立即生效,不存在延时行为。

变量常用在实现某种运算的赋值语句中。变量赋值和初始化赋值符号用“:=”表示。

变量不能用于硬件连线和存储元件。

**************************************************

PROCESS内部各语句之间是顺序关系 。在系统仿真时, PROCESS语句是按书写顺序一条一条向下执行的。而不象BLOCK中的语句可以并行执行。

  若构造体中有多个进程存在,各进程之间的关系是并行关系 ;进程之间的通信则一边通过接口由信号传递,一边并行地同步执行。
                  if(rst='1') then
                    --如果复位信号有效,那么读取inst_rom.data文件初始化imem

                          k := 0;
                          while not endfile(f1) loop
                                 readline(f1,li);
                                 i := 0;

                                 while i<8 loop
                                         read(li,j,good);
                                         if(good) then
                                                temp(31-i*4 downto 28-i*4) := conv_character_to_std_logic_vector(j);
                                         end if;
                                         i := i+1;
                                 end loop;

                                 mem0(k) <= temp;
                                 k := k+1;
                          end loop;
                          data <= (others=>'0');
                        else

                           --复位信号无效的时候就是正常读取,依据地址给出指令
                           --注意的是地址的最低两位不用


               temp1 := conv_integer(addr(IMEMBIT+2 downto 2));

                     data <= mem0(temp1);       
                  end if;

内容比较多,首先区分信号和变量

24.JPG

25.JPG

以及转换函数,
26.JPG

虽然没有去看LOOP循环,但是基本能理解。
这样下来,imem功能很容易理解,就是上面的注释:
1、如果复位信号有效,那么读取inst_rom.data文件初始化imem;
2、复位信号无效的时候就是正常读取,依据地址给出指令。


***************************************************************

第一个文件imem.vhd 先这样完成第一遍理解。
发表于 2014-1-20 20:13:59 | 显示全部楼层
楼主太给力了,解释的这么详细
 楼主| 发表于 2014-1-21 07:47:04 | 显示全部楼层
回复 3# leishangwen

你的开发笔记给了我很多思路,很适合入门,对帮助学习MC8051作用也很大。
 楼主| 发表于 2014-1-21 08:04:09 | 显示全部楼层
**********************************
从OpenMIPS再到MC8051的局部结构,

OpenMIPS ---> MC8051

imem文件的启发
**********************************

先回顾一下MC8051的一个局部结构,

27.JPG

现在有一个想法,OpenMIPS的几个文件是否也可以使用这个结构表示?答案是可以。

下面几个文件也对应(control_mem和control_fsm 是最复杂的,只是感觉应该对应)
28.JPG

那么OpenMIPS的结构就可以

29.JPG

因为OpenMIPS内容比较少,好学习;通过OpenMIPS和MC8051 在结构上的类似,来辅助对MC8051的学习,初步打算:
1、裁减MC8051代码,只运行一个命令;
2、先OpenMIPS学习,然后同结构MC8051的学习(即交叉学习)。
 楼主| 发表于 2014-1-21 08:36:20 | 显示全部楼层
本帖最后由 oldbeginner 于 2014-1-21 08:45 编辑

通过代码理解OpenMIPS的层次关系,

要理解上面的层次关系,必须看代码,代码中有compent 命令,是理解的关键,

30.JPG

而有compent 后,同时需要理解端口映射

31.JPG

***************************************************

在 OpenMIPS_min_sopc_tb.vhd 文件中,该文件为仿真驱动文件,


component OpenMIPS_min_sopc port (
    clk    : in  std_logic;
    rst   : in  std_logic
  );
end component;

即调用了OpenMIPS_min_sopc 模块。

*************************************************

在 OpenMIPS_min_sopc.vhd 文件中,

有模块
entity OpenMIPS_min_sopc is
  port (
    clk    : in  std_logic;
    rst   : in  std_logic
  );
end;
该模块被tb文件调用。

同时还有,
  component OpenMIPS port(
    clk    : in  std_logic;
    rst   : in  std_logic;
    imem_addr : out word;
    imem_data : in  word

  );
  end component;
  
  component imem port (
    rst   : in  std_logic;
          addr  : in  word;
          data  : out word
        );
        end component;

即调用了两个模块 OpenMIPS 模块和 imem 模块。

****************************************

imem模块之前已经理解了一遍,它是初始化ROM用到,没有再调用其它模块。

在 OpenMIPS.vhd 文件中,

entity OpenMIPS is
  port (
    clk    : in  std_logic;
    rst   : in  std_logic;
    imem_addr : out word;
    imem_data : in  word

  );
end;
定义了OpenMIPS 模块,并被 OpenMIPS_min_sopc 调用。

然后还有,
        component iu   port (
                 clk   : in  std_logic;
                 rst  : in  std_logic;
     imem_addr : out word;
     imem_data : in  word;

                 rf_o   : out iregfile_in_type;            
                 rf_i   : in  iregfile_out_type
                 );
        end component;

        component regfile   port (
                 clk   : in  std_logic;
                 rst   : in  std_logic;
                 waddr  : in  std_logic_vector(4 downto 0);
                 wdata  : in  word;
                 we     : in  std_logic;
                 raddr1 : in  std_logic_vector(4 downto 0);
                 re1    : in  std_logic;
                 rdata1 : out word;
                 raddr2 : in  std_logic_vector(4 downto 0);
                 re2    : in  std_logic;
                 rdata2 : out word
          );
        end component;

即调用了 iu  和 regfile模块。

*******************************
至此OpenMIPS的层次架构就如图所示,基本和MC8051一致。
29.JPG
既然可以利用OpenMIPS 来辅助 MC8051 的学习,那么 imem 文件已经学过了,
就可以对应地学习 mc8051_rom 了。
 楼主| 发表于 2014-1-21 09:20:05 | 显示全部楼层
*********************************
对MC8051中的 ROM模型的学习

*********************************

在MC8051的user guide文件中,讲到文件名命名规则如下,正好配合mc8051_rom 一起理解。
32.JPG

根据上面的命名规则,mc8051_rom模块对应三个文件,分别是:
1、mc8051_rom_.vhd;
2、mc8051_rom_sim.vhd;
3、mc8051_rom_sim_cfg.vhd

这里有点例外,rtl 被替换为 sim,因为这个rom 模块主要是为了仿真使用的。

******************
--mc8051_rom_.vhd
--Description: The mc8051 ROM model.
******************
-------------------------------------------------------------------------------
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_arith.all;
use IEEE.std_logic_textio.all;
library STD;
use STD.textio.all;

------------------------ ENTITY DECLARATION -------------------------
entity mc8051_rom is

  generic (c_init_file : string := "mc8051_rom.dua");

  port (clk        : in  std_logic;                            -- clock signal
        reset      : in  std_logic;                            -- reset signal
        rom_data_o : out std_logic_vector(7 downto 0);    -- data output
        rom_adr_i  : in  std_logic_vector(15 downto 0));  -- adresses

end mc8051_rom;
-------------------------------------------------------------------------------
*************************************************
33.JPG

上面的实体定义,多了一个generic 命令,需要理解一下,
34.JPG

再继续看
********************
--mc8051_rom_sim.vhd;
--Description: The mc8051 ROM model.
**********************
-------------------------------------------------------------------------------
architecture sim of mc8051_rom is

   type   rom_type is array (65535 downto 0) of bit_vector(7 downto 0);
   signal s_init : boolean := false;

begin

------------------------------------------------------------------------------
-- rom_read
------------------------------------------------------------------------------

  p_read : process (clk, reset, rom_adr_i)
      variable v_loop : integer;   
      variable v_line : line;
      variable v_rom_data : rom_type;
      file f_initfile : text is in c_init_file;

  begin

    if (not s_init) then
      v_loop := 0;

      while ((not endfile(f_initfile) and (v_loop < 65535))) loop
        readline(f_initfile,v_line);
        read(v_line,v_rom_data(v_loop));
        v_loop := v_loop + 1;        
      end loop;

      s_init <= true;
    end if;

    if (clk'event and (clk = '1')) then  -- rising clock edge
      rom_data_o <= to_stdlogicvector(v_rom_data(conv_integer(unsigned(rom_adr_i))));
    end if;

  end process p_read;

end sim;
---------------------------------------------------------------------------------------------
***********************************************************
感觉和imem有些类似,
这里看一下loop语句,比较简单
35.JPG

而clk'event 的理解需要一点资料
36.JPG

37.JPG

这样clk'event 也可以理解了。

最长的一句
    rom_data_o <= to_stdlogicvector(v_rom_data(conv_integer(unsigned(rom_adr_i))));
字面上就可以理解,
把地址中的数据赋给data,复杂之处只是数据类型变来变去。
**********************************************

再继续看,
*************************
--mc8051_rom_sim_cfg.vhd
--Description: The mc8051 ROM model
****************************
-------------------------------------------------------------------------------
configuration mc8051_rom_sim_cfg of mc8051_rom is
  for sim
  end for;
end mc8051_rom_sim_cfg;
--------------------------------------------------------------------------
**********************************************
需要理解configuration
38.JPG

39.JPG

*************************************************************

这样MC8051  的 ROM模型就完成了第一遍理解,这也是最简单的一个模型。
该模型不需要裁减。
发表于 2014-1-21 16:25:29 | 显示全部楼层
,多谢楼主的文章,很好
 楼主| 发表于 2014-1-22 09:20:03 | 显示全部楼层
本帖最后由 oldbeginner 于 2014-1-22 09:26 编辑

OpenMIPS最难的两个文件是iu.vhd 和 regfile.vhd ,如果能理解了这两个文件,大概就解决了90%的难度。

不过,再理解这两个文件之前,先看一个文件stdlib.vhd 。根据名称看,是标准库的意思,确实,这里有大量的定义,类似c语言的宏定义。

打开文件
****************************
-- Package:         stdlib
-- File:        stdlib.vhd
-- Author:        Lei Silei
-- Description:        OpenMIPS library(type,funciton,etc)
------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
use ieee.std_logic_arith.all;

package stdlib is

*****************************************

需要理解什么是package

40.JPG

然后继续看代码,

***************************************
constant IMEMSIZEINWORD : integer := 1023;
。。。。
constant EXE_ORI   : std_logic_vector(5 downto 0) := "001101";
。。。。

subtype word is std_logic_vector(31 downto 0);
******************************************

通过constant 定义了很多常量;
41.JPG

word是一个子类型,(我还以为是默认的关键字)

subtype word is std_logic_vector(31 downto 0);
42.JPG

然后继续阅读代码,

*************************************
  --iu与Regfile之间的接口信号,相对Regfile而言是输入
        type iregfile_in_type is record
          raddr1        : std_logic_vector(4 downto 0); -- read address 1
          raddr2        : std_logic_vector(4 downto 0); -- read address 2
          waddr        : std_logic_vector(4 downto 0); -- write address
          wdata         : std_logic_vector(31 downto 0); -- write data
          ren1   : std_logic;                         -- read 1 enable
          ren2   : std_logic;                         -- read 2 enable
          wren   : std_logic;                         -- write enable
        end record;
*******************************************

又要学习什么是record了?
43.JPG

接口信号表示如下,
44.JPG

继续读代码,
*************************************
  --iu与Regfile之间的接口信号,相对Regfile而言是输出
        type iregfile_out_type is record
          data1            : std_logic_vector(31 downto 0); -- read data 1
          data2            : std_logic_vector(31 downto 0); -- read data 2
        end record;
*************************************
上述接口信号在上图已表达出,继续

*************************************
  subtype pctype is std_logic_vector(31 downto 2);
  
  --只有32个寄存器,所以寄存器地址只有5位
  subtype rfatype is std_logic_vector(4 downto 0);

***************************************
又是两个subtype
定义,pctype字面意思好理解,rfatype 字面意思不好理解。regfile a type ?a是什么?

暂时继续,
***************************************
  --取指阶段的寄存器
  type fetch_reg_type is record                     
    pc     : pctype;  --要读取的指令地址
  end record;
*************************************


记住fetch_reg_type,记号F

继续,
****************************************
  --译码阶段的寄存器
  type decode_reg_type is record                  
    pc     : pctype;  --处于译码阶段的指令地址
    inst   : word;    --处于译码阶段的指令
  end record;
****************************************


记住decode_reg_type,记号D

继续,
**************************************
  --执行阶段的寄存器
  type execute_reg_type is record                              
    rd    : rfatype;         --要写入的目的寄存器
    wreg  : std_logic;       --是否要写入目的寄存器
    rfe1       : std_logic;  --是否要读取源寄存器1
    rfe2       : std_logic;  --是否要读取源寄存器2
    rfa1       : rfatype;    --要读取的源寄存器1的地址
    rfa2       : rfatype;    --要读取的源寄存器2的地址
    reg1       : word;       --读取到的源寄存器1的值
    reg2       : word;       --读取到的源寄存器2的值
    imm        : word;       --指令需要的立即数的值
    cnt    : std_logic_vector(1 downto 0);    --是否是多周期指令
    aluop  : std_logic_vector(7 downto 0);          --ALU的操作类型
    alusel : std_logic_vector(2 downto 0);          --ALU的运算结果选择信号
          inst_valid : std_logic;  --指令是否有效
  end record;

****************************************

内容好多,怎么记?rfe rfa 字面上无法理解,需要看注释。

先继续,
************************************
  --访存阶段的寄存器
  type memory_reg_type is record                                               
    waddr : rfatype;         --要写入的目的寄存器
    wreg  : std_logic;       --是否要写入目的寄存器
    result : word;           --要写入目的寄存器的值
  end record;
***********************************


寄存器M;

继续,
*****************************************
  --回写阶段的寄存器
  type write_reg_type is record                                    
    result : word;           --要写入目的寄存器的值
    waddr  : rfatype;        --要写入目的寄存器
    wreg   : std_logic;      --是否要写入目的寄存器
  end record;
************************************


寄存器 W。
然后是寄存器缩写
*******************
  type registers is record                                                        
    f  : fetch_reg_type;
    d  : decode_reg_type;
    e  : execute_reg_type;
    m  : memory_reg_type;
    w  : write_reg_type;
  end record;
***********************

这样五个寄存器的定义就结束了,最重要的是注释,例如r.e.rfe1 字面上很难理解,需要看这里的注释。

然后,就是函数定义,
******************************
  function conv_character_to_std_logic_vector(c : character) return std_logic_vector;
end;  --表示包头结束
********************************

定义了字符转向量的函数,然后包头结束,
下面继续包体,包体 则用来存放说明中的函数和子程序
*******************************
package body stdlib is

        function conv_character_to_std_logic_vector(c : character) return std_logic_vector is
        begin
          case c is
                        when '0' => return "0000";
                        when '1' => return "0001";
                        when '2' => return "0010";
                        when '3' => return "0011";
                        when '4' => return "0100";
                        when '5' => return "0101";
                        when '6' => return "0110";
                        when '7' => return "0111";
                        when '8' => return "1000";
                        when '9' => return "1001";
                        when 'A' | 'a' => return "1010";
                        when 'B' | 'b' => return "1011";
                        when 'C' | 'c' => return "1100";
                        when 'D' | 'd' => return "1101";
                        when 'E' | 'e' => return "1110";
                        when 'F' | 'f' => return "1111";
                        when others => return "0000";
           end case;   
   end;

end;
**********************************************


这样,函数的功能一目了然。
在使用时,注意以下就可以了
45.JPG
 楼主| 发表于 2014-1-22 10:24:53 | 显示全部楼层
MC8051 和 stdlib.vhd 文件对应的是

mc8051_p.vhd。因为要裁减MC8051,只执行一两个命令就可以了,该文件需要大量裁减。

先初步裁减一下
首先,
***************************************************************
--Description: Collection of constants, types, and components.

-------------------------------------------------------------------------------
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_arith.all;

package mc8051_p is

*********************************************************

熟悉的开场,

继续,
删除
  -----------------------------------------------------------------------------
  -- Set data width of mc8051_alu (no other than 8 supported at the moment!)
  -- Default: 8
  constant C_DWIDTH : integer := 8;
  -----------------------------------------------------------------------------
类似这样的语句,目前不需要alu,命令只有ori 或者 move d #data。所以乘法等都不需要。

***************************************
--暂时只保留
constant MOV_D_DATA    : std_logic_vector(7 downto 0) := "01110101";
constant ORL_D_DATA    : std_logic_vector(7 downto 0) := "01000011";
*******************************************

到时再根据难度,选择简单易懂的指令,
先留意一下,等到fsm mem时再理解。

继续,
**************************
  type t_state is (STARTUP,
                   FETCH,
                   EXEC1,
                   EXEC2,
                   EXEC3);
*************************

不需要裁减,
继续,
这一个指令集合需要裁减,只用1、2个。
************************
type t_instr_category is (IC_MOV_D_DATA, IC_ORL_D_DATA);
**************************************************


然后,很多模块,大多数不会用到,只保留
************************************
component control_fsm
port ( state_i     : in t_state;    -- actual state
。。。。。
end component;

  component control_mem
    port (pc_o           : out std_logic_vector(15 downto 0);
。。。。。
  end component;
************************************


模块的输入和输出好多,因为只运行一个指令,肯定有很多用不到,可以大量裁减。只是从字面上无法辨认出哪些输入和输出不会用到,还需要结合fsm和 mem具体内容来裁减。
这里是裁减的重点。


同样,还有
****************************
  component mc8051_control
    port (pc_o           : out std_logic_vector(15 downto 0);
。。。。。
end component;

  component mc8051_core
    port (clk         : in  std_logic;
。。。。。
end component;

  component mc8051_top
    port (clk         : in  std_logic;
。。。。。
end component;

*********************************

上面三个模块的裁减同样依赖于fsm和mem的裁减结果。

最后,ROM模块
********************************
  -----------------------------------------------------------------------------
  -- START: Component declarations for simulation models
  -----------------------------------------------------------------------------
  component mc8051_rom
    port (clk        : in  std_logic;
          reset      : in  std_logic;
          rom_data_o : out std_logic_vector(7 downto 0);
          rom_adr_i  : in  std_logic_vector(15 downto 0));
   
  end component;
  -----------------------------------------------------------------------------
  -- END: Component declarations for simulation models
  -----------------------------------------------------------------------------
end mc8051_p;
****************************************


该文件还剩下component无法裁减,需要确定fsm和mem模块的输入输出后,才可以进行裁减。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

站长推荐 上一条 /2 下一条

×

小黑屋| 手机版| 关于我们| 联系我们| 在线咨询| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2024-11-24 12:09 , Processed in 0.038738 second(s), 11 queries , Gzip On, Redis On.

eetop公众号 创芯大讲堂 创芯人才网
快速回复 返回顶部 返回列表