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

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

手机号码,快捷登录

手机号码,快捷登录

找回密码

  登录   注册  

快捷导航
搜帖子
查看: 9309|回复: 15

开源软核学习笔记01(先从8051开始再说)——2014_1_4

[复制链接]
发表于 2014-1-4 22:04:56 | 显示全部楼层 |阅读模式

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

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

x
我是被
10天实现处理器——OpenMIPS开发笔记
吸引来的,但是mips linux等辅助工具让我感觉很难上手。

我目前的基础是,去年8月开始学习单片机的,因为资料非常丰富,进展很顺利。Verilog和quartus前几天才开始学习,因为对51比较熟悉(51汇编还不会),所以打算以开源的8051软核作为开始,verilog或VHDL无所谓,反正都是刚开始学习,有人说verilog和c类似,我没感觉出来。数字电路自学了一个多星期,基本明白触发器工作原理(多亏这本书《编码:隐匿在计算机软硬件背后的语言》,才让我对数字电路有了个形象认识)。

先从oc8051开始,源码下载
http://bbs.eetop.cn/thread-221953-1-3.html

主要原因是verilog,网上认为比VHDL容易入门。

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

CPU设计入门教程好像都是MIPS的,没有看到8051作为入门教程的,找了一些相关论文,也不适合入门。
不管怎样,
《计算机原理与设计》,李亚民;http://pan.baidu.com/share/link?shareid=2622740890&uk=4061321394&fid=155890685
《Verilog_HDL复杂数字系统设计-本2011》,南通大学PPT;http://wenku.baidu.com/link?url=gRj21PCVXUNyaNVP6aICzRVItTUG7DqHXtwh3pc_9HANXsZTTBrIv_WlFvKCE3CNsfeQp7Gnt0-0EkwpvzievJuq4ZgElEL5b9WUSPSnX0i
作为辅助教材。

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

另外,虽然有源码,但是没有找到如何移植的方法,所以还没有仿真过。这就是现状。
发表于 2014-1-4 22:13:24 | 显示全部楼层
支持一下,我之前也有一些资料,楼主可以参考
DE2上使用OC8051运行点灯程序
http://download.csdn.net/detail/leishangwen/5173363
 楼主| 发表于 2014-1-4 22:26:05 | 显示全部楼层
虽然verilog一开始就是从上而下的设计方法,因为我还在入门阶段,没有办法高屋建瓴,只能采用学51的方法,先理解其中一个模块再说(先捏软柿子)。

以字母顺序,ACC排名靠前,代码不是很多,可以作为第一个理解的模块。
遍学遍搜,真不错
http://zh.wikipedia.org/wiki/%E7%B4%AF%E5%8A%A0%E5%99%A8,wiki点击英文,居然还有acc_based comuters历史。
http://blog.163.com/gxhy_lyf/blog/static/170345637201188112155853/

ACC(Accumulator)是累加器A缩写。累加器A是一个具有特殊用途的二进制8位寄存器,专门用来存放操作数或运算结果。

用语言表达不如图片,
计算机结构.gif   

图片不如动画,

    现在我们来编写5CH+2E的程序。根据表中提供的指令,用助记符书写的程序如下:
      MOV  AL,5CH      ;第一个操作数(5CH)送到累加器

      ADD  AL,2EH      ;把累加器的内容与第2个操作数(2EH)相加,结果(8AH)送到仍送到累加器


指令执行过程


动画背景颜色怎么变成白色了?下面有动画链接。
http://mcit.xjtu.edu.cn/wlkj/wykj/ch2/ex2_1_2.swf


1、第一条指令的取指阶段。

2、然后转入指令的译码和执行阶段。

3、
CPU开始执行第二条指令。
 楼主| 发表于 2014-1-4 22:28:25 | 显示全部楼层
回复 2# leishangwen


    好的,非常感谢你的笔记,虽然还没搞懂,但是已经感兴趣了。
 楼主| 发表于 2014-1-4 22:54:35 | 显示全部楼层
本帖最后由 oldbeginner 于 2014-1-4 23:01 编辑

oc8051有几个版本,先看ver1

**************************************
module oc8051_acc (clk, rst, bit_in, data_in, data2_in, wr, wr_bit, wad2, wr_addr,
data_out, p);

input clk, rst, wr, wr_bit, wad2, bit_in;
input [7:0] wr_addr, data_in, data2_in;

output p;
output [7:0] data_out;

reg [7:0] data_out;

assign p = ^data_out;

always @(posedge clk or posedge rst)
begin
  if (rst)
    data_out <= #1 `OC8051_RST_ACC;
  else if (wad2)
    data_out <= #1 data2_in;
  else
    case ({wr, wr_bit})
      2'b10: begin
        if (wr_addr==`OC8051_SFR_ACC)
          data_out <= #1 data_in;
      end
      2'b11: begin
        if (wr_addr[7:3]==`OC8051_SFR_B_ACC)
          data_out[wr_addr[2:0]] <= #1 bit_in;
      end
    endcase
end

endmodule

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

再看ver1.13

module oc8051_acc (clk, rst,
                 bit_in, data_in, data2_in,
         data_out,
         wr, wr_bit, wr_addr,
         p, wr_sfr);


input clk, rst, wr, wr_bit, bit_in;
input [1:0] wr_sfr;
input [7:0] wr_addr, data_in, data2_in;

output p;
output [7:0] data_out;

reg [7:0] data_out;
reg [7:0] acc;

wire wr_acc, wr2_acc, wr_bit_acc;

assign p = ^acc;

assign wr_acc     = (wr_sfr==`OC8051_WRS_ACC1) | (wr & !wr_bit & (wr_addr==`OC8051_SFR_ACC));
assign wr2_acc    = (wr_sfr==`OC8051_WRS_ACC2);
assign wr_bit_acc = (wr & wr_bit & (wr_addr[7:3]==`OC8051_SFR_B_ACC));

always @(wr_sfr or data2_in or wr2_acc or wr_acc or wr_bit_acc or wr_addr[2:0] or data_in or bit_in or data_out)
begin
  if (wr2_acc)
    acc = data2_in;
  else if (wr_acc)
    acc = data_in;
  else if (wr_bit_acc)
    case (wr_addr[2:0]) /* synopsys full_case parallel_case */
      3'b000: acc = {data_out[7:1], bit_in};
      3'b001: acc = {data_out[7:2], bit_in, data_out[0]};
      3'b010: acc = {data_out[7:3], bit_in, data_out[1:0]};
      3'b011: acc = {data_out[7:4], bit_in, data_out[2:0]};
      3'b100: acc = {data_out[7:5], bit_in, data_out[3:0]};
      3'b101: acc = {data_out[7:6], bit_in, data_out[4:0]};
      3'b110: acc = {data_out[7],   bit_in, data_out[5:0]};
      3'b111: acc = {bit_in, data_out[6:0]};
    endcase
  else
    acc = data_out;
end

always @(posedge clk or posedge rst)
begin
  if (rst)
    data_out <= #1 `OC8051_RST_ACC;
  else
    data_out <= #1 acc;
end

`ifdef OC8051_SIMULATION

always @(data_out)
  if (data_out===8'hxx) begin
    $display("time ",$time, "   faulire: invalid write to ACC (oc8051_acc)");
#22
    $finish;

  end


`endif

endmodule

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

ver1.13功能变复杂了,代码也复杂了。
还是用ver1版本完成第一遍理解再说。  

复习一下verilog格式

module 模块名1(输入输出端口2)

input 输入3
output 输出4
reg 定义寄存器5
assign 赋值6

always@触发条件7

begin
   核心功能8
end

endmodule
 楼主| 发表于 2014-1-4 23:31:51 | 显示全部楼层
本帖最后由 oldbeginner 于 2014-1-5 15:28 编辑

模块名1:oc8051_acc

输入输出端口2:clk, rst, bit_in, data_in, data2_in, wr, wr_bit, wad2, wr_addr, data_out, p

输入3:clk, rst, wr, wr_bit, wad2, bit_in;
输入3:[7:0] wr_addr, data_in, data2_in;

输出4:p;
输出4:[7:0] data_out;   

定义寄存器5:[7:0] data_out;

赋值6: p = ^data_out;

触发条件7:posedge clk or posedge rst

上面这些定义都是中规中距的,没有难度。
***************************************

核心功能8:

  if (rst)
    data_out <= #1 `OC8051_RST_ACC;
  else if (wad2)
    data_out <= #1 data2_in;
  else
    case ({wr, wr_bit})
      2'b10: begin
        if (wr_addr==`OC8051_SFR_ACC)
          data_out <= #1 data_in;
      end
      2'b11: begin
        if (wr_addr[7:3]==`OC8051_SFR_B_ACC)
          data_out[wr_addr[2:0]] <= #1 bit_in;
      end
    endcase

这里分成了四种情况:
1、if(rst),如果是复位,则data_out <= #1 `OC8051_RST_ACC;
     其中,`define OC8051_RST_ACC 8'h00 // accumulator (在文件oc8051_defines.v中)
     <= 是块结束赋值,#1延迟1个单位时间,当这两个条件满足后,data_out = 8'h00 (简单些data_out = 0)。

2、 else if (wad2),
    // wad2         write data 2,写数据2,这是什么?先继续。
    则data_out <= #1 data2_in;
    即块结束,延迟1单位时间,data_out = data2_in,这样数据就写进了。

3、wr=1并且wr_bit=0时,
    // wr           write - actine high,什么是actine,没有这个单词
   // wr_bit       write bit addresable - actine high
   再如果满足wr_addr==`OC8051_SFR_ACC,要求真多
   其中,`define OC8051_SFR_ACC 8'he0 //accumulator(在文件oc8051_defines.v中)
   则,data_out <= #1 data_in;
   即块结束,延迟1单位时间,data_out = data_in

4、wr=1并且wr_bit=1时,
    并且满足wr_addr[7:3]==`OC8051_SFR_B_ACC
    其中,`define OC8051_SFR_B_ACC 5'b11100 //accumulator
   则,data_out[wr_addr[2:0]] <= #1 bit_in;
    其中,// wr_addr      write address (if is addres of acc and white high must be written to acc)
    其中,// bit_in       bit input - used in case of writing bits to acc (bit adddressable memory space - alu carry)
   即块结束,延迟1单位时间,data_out[wr_addr[2:0]]=bit_in

代码读完,对整个过程还是不清楚,还需要找一个实例来帮助理解。

在top中的定义
oc8051_acc oc8051_acc1(.clk(clk), .rst(rst), .bit_in(desCy), .data_in(des1), .data2_in(des2_s), .wr(wr_r), .wr_bit(bit_addr_r), .wad2(wad2_r), .wr_addr(wr_addr), .data_out(acc), .p(p));
 楼主| 发表于 2014-1-5 14:09:58 | 显示全部楼层
会用到ACC的是几种寻址方式,

单片机的立即寻址方式


单片机的寄存器寻址方式


单片机的变址寻址方式
   

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

从上面的动画来看,ACC的数据和ROM模块关系密切,



需要看一下ROM模块(都只用ver1版作为第一遍理解,感觉代码少,功能少)

module oc8051_rom_addr_sel (clk, rst, select, des1, des2, pc, op1, out_data, out_addr);

input clk, rst, select;
input [7:0] des1, des2, op1;
input [15:0] pc;
output [7:0] out_data;
output [15:0] out_addr;

reg sel_buff;

assign out_data = sel_buff ? op1 : des2;
assign out_addr = select ? {des2, des1} : pc;

always @(posedge clk or posedge rst)
begin
  if (rst)
    sel_buff <= #1 1'b0;
  else
    sel_buff  <= #1 select;
end

endmodule

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

module 模块名1(输入输出端口名2)

input 输入3;

output 输出4;

reg 定义寄存器5;

assign 赋值6;

always@触发条件7

begin
   核心功能8;
end

endmodule
 楼主| 发表于 2014-1-5 14:36:19 | 显示全部楼层
本帖最后由 oldbeginner 于 2014-1-5 14:48 编辑

模块名1:oc8051_rom_addr_sel

输入输出端口名2:clk, rst, select, des1, des2, pc, op1, out_data, out_addr

输入3:clk, rst, select;
输入3:[7:0] des1, des2, op1;
输入3:[15:0] pc;

输出4:[7:0] out_data;
输出4:[15:0] out_addr;

定义寄存器5:sel_buff;

赋值6:out_data = sel_buff ? op1 : des2;
赋值6:out_addr = select ? {des2, des1} : pc;

其中,// output data is operation byte 1,out_data(输出数据)是操作字节,当sel_buff为真时,输出数据为op1,否则就是des2
// op1          byte 1 from rom,op1是从ROM里来的一个字节
// des2   alu destination input,des2是来自alu(算术逻辑单元)。

// select       output select,
// output address is alu destination,out_addr是算术逻辑单元地址,当select为真时,拼接des2和des1作为地址,否则选用pc地址。
// (instructions MOVC)

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

触发条件7:posedge clk or posedge rst

核心功能8:
  if (rst)
    sel_buff <= #1 1'b0;
  else
    sel_buff  <= #1 select;
   
核心功能是通过sel_buff赋值,来影响,赋值6:out_data = sel_buff ? op1 : des2;
如果复位,sel_buff = 0
否则,sel_buff = select

模块的主要负责从ROM中取得指令代码/操作数。
似乎还可以理解,要是有现成的动画就好了。
 楼主| 发表于 2014-1-5 16:09:33 | 显示全部楼层
因为oc8051上输入输出定义名称和我找到课件名称不一致,目前还不能整合成图片形式。再多看几个定义,熟悉以后再整合。

现在开始理解ALU,搜一下,
http://baike.baidu.com/link?url=zz3Orwr4DV1IkTbxiUFe8KxwJcu7Z1wzBjs_Sq8ibJkeZeaCBOJ4v8b3d175HQUn
百度百科上营养不足,
算术逻辑单元(arithmetic logic unit,缩写ALU)是进行整数运算的结构。现阶段是用电路来实现,应用在电脑芯片中。
01.JPG

******************************************
开始看oc代码,ALU有三个选择来源,先看第一个

module oc8051_alu_src1_sel (sel, immediate, acc, ram, ext, des);

input [1:0] sel; input [7:0] immediate, acc, ram, ext;
output [7:0] des;
reg [7:0] des;

always @(sel or immediate or acc or ram or ext)
begin
  case (sel)
    `OC8051_ASS_RAM: des= ram;
    `OC8051_ASS_ACC: des= acc;
    `OC8051_ASS_XRAM: des= ext;
    `OC8051_ASS_IMM: des= immediate;
    default: des= 2'bxx;
  endcase
end

endmodule

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

module 模块名1(输入输出端口名2)

input 输入3;

output 输出4;

reg 定义寄存器5;

assign 赋值6;

always@触发条件7

begin
   核心功能8;
end

endmodule
****************************************

模块名1:oc8051_alu_src1_sel

输入输出端口名2:sel, immediate, acc, ram, ext, des

输入3:[1:0] sel; input [7:0] immediate, acc, ram, ext;

输出4:[7:0] des;

定义寄存器5:[7:0] des;

触发条件7:sel or immediate or acc or ram or ext

上面的代码没有难度,
重要的是定义,定义非常明确
// sel          select signals (from decoder, delayd one clock)
// immediate    input from immediate_sel1
// acc          acc input
// ram          ram input
// ext          external ram input
// des          output (alu sorce 1)

核心功能8:
  case (sel)
    `OC8051_ASS_RAM: des= ram;
    `OC8051_ASS_ACC: des= acc;
    `OC8051_ASS_XRAM: des= ext;
    `OC8051_ASS_IMM: des= immediate;
    default: des= 2'bxx;
  endcase


其中,在
// alu source select
//
`define OC8051_ASS_RAM 2'b00 // RAM
`define OC8051_ASS_ACC 2'b01 // accumulator
`define OC8051_ASS_XRAM 2'b10 // external RAM -- source1
`define OC8051_ASS_IMM 2'b11 // immediate data -- source1

根据输入(sel)的来源,设置相应的输出地址des。

用同样的方法理解另外两个模块
***********************************************

module oc8051_alu_src2_sel (sel, op2, acc, ram, des);

input [1:0] sel; input [7:0] acc, ram, op2;
output [7:0] des;
reg [7:0] des;

always @(sel or op2 or acc or ram)
begin
  case (sel)
    `OC8051_ASS_RAM: des= ram;
    `OC8051_ASS_ACC: des= acc;
    `OC8051_ASS_ZERO: des= 8'h00;
    `OC8051_ASS_OP2: des= op2;
    default: des= 2'bxx;
  endcase
end

endmodule

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

module oc8051_alu_src3_sel (sel, pc, dptr, out);

input sel;
input [7:0] pc, dptr;
output [7:0] out;

assign out = sel ? pc : dptr;

endmodule

最后一个模块需要看一下注释
// sel          select signals (from decoder, delayd one clock)
// pc           program counter input
// dptr         data pointer input
// out          output (alu sorce 2)  

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

完成准备工作,就可以开始理解ALU功能了。
 楼主| 发表于 2014-1-5 16:44:34 | 显示全部楼层
本帖最后由 oldbeginner 于 2014-1-5 16:53 编辑

module oc8051_alu (op_code, src1, src2, src3, srcCy, srcAc, des1, des2, desCy, desAc, desOv, bit_in);

input srcCy, srcAc, bit_in; input [3:0] op_code; input [7:0] src1, src2, src3;
output desCy, desAc, desOv;
output [7:0] des1, des2;

  reg desCy, desAc, desOv;
  reg [7:0] des1, des2;

  reg [4:0] add1, add2, add3, add4; reg [3:0] add5, add6, add7, add8; reg [1:0] add9, adda, addb, addc;

always @(op_code or src1 or src2 or srcCy or srcAc or bit_in or src3 or mulsrc1 or mulsrc2 or mulOv or divsrc1 or divsrc2 or divOv)
begin

  case (op_code)
//operation add
    `OC8051_ALU_ADD: begin
      add1= {1'b0,src1[3:0]};
      add2= {1'b0,src2[3:0]};
      add3= {3'b000,srcCy};
      add4= add1+add2+add3;

      add5={1'b0,src1[6:4]};
      add6={1'b0,src2[6:4]};
      add7={1'b0,1'b0,1'b0,add4[4]};
      add8=add5+add6+add7;

      add9={1'b0,src1[7]};
      adda={1'b0,src2[7]};
      addb={1'b0,add8[3]};
      addc=add9+adda+addb;

      des1={addc[0],add8[2:0],add4[3:0]};
      des2=src3+addc[1];  ///ti , tole se ni uredu...
      desCy=addc[1];
      desAc=add4[4];
      desOv=addc[1] ^ add8[3];

    end

  其它算法

  endcase

end

endmodule

*********************************************
太长了,把其它算法暂时省略了,只看一个加法。

这时知道基础的重要性了,
先看一下
算术运算单元ALU的设计
http://wenku.baidu.com/view/c84c3040a8956bec0975e3bd.html

也有用quartus
http://wenku.baidu.com/view/21b3f11fa76e58fafab00349.html

原来设计ALU都可以作为一个课程设计,看来目前暂时肤浅理解就可以了(运算的输入和输出,不需要理解怎样运算),不再深入。

另外,关于行为级和门级的区别,我认为上面的ALU模块就是门级的,行为级是可以用肤浅的话描述的(类似项目经理布置的任务说明),而门级必须说清楚是怎样怎样的。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

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


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

GMT+8, 2024-12-28 13:15 , Processed in 0.035922 second(s), 9 queries , Gzip On, Redis On.

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