显示页面 讨论 修订记录 反向链接 本页面只读。您可以查看源文件,但不能更改它。如果您觉得这是系统错误,请联系管理员。 ======OC8051====== 我们以开源IP分享网站 opencores.org 上面的一款开源8051核-oc8051为基础,介绍8051的结构、工作原理、硬件描述的实现及在FPGA上的仿真、移植过程。 \\ oc8051下载地址:https://opencores.org/project/8051 \\ ====1 关于OC8051==== oc8051与8051微控制器是兼容的。所谓兼容性是指oc8051使用相同的指令集。实现起来当然各有差异。 首先,最重要的区别是有两个阶段的流水线操作。在第一周期指令及其操作数被取出和解码时,第二周期用于计算结果并将其写入存储器。我们通过额外的寄存器来实现这一点,其唯一的任务是将信号延迟一个时钟周期。这是必须的,因为我们的想法是我们已经在第一个周期中设置了所有控制信号,然后将一个不需要的控制信号(例如,保存结果的地址)延迟一个周期。 因为我们可能还需要指令的第二个和第三个字节,所以我们使用24位宽总线的程序ROM。由于处理器设计的需要,我们还使用能够同时写入和读取的内部存储器。 其中一个重要方面是总线控制,由主模块(oc8051_decoder)处理。我们通过控制信号来实现这一点,控制信号连接到在每个总线开始时设置的多路复用器。有了多路复用器,我们设置了谁控制总线 ===1.1 oc8051文件组织目录=== * oc8051 * asm 软件相关文件 * hex * in * v * vec * bench 仿真相关文件 * verilog * doc 说明文档 * pdf * src * rtl verilg代码 * verilog * sim RTL仿真文件 * rtl_sim * sw ROM生成工具 * source * syn 工程文件 * src ===1.2 oc8051的接口=== {{::oc8051-接口.gif?380|}} {{::oc8051-接口描述.gif.png|}} \\ ====2 模块描述==== ===2.1 oc8051_top=== Ports: * -rst reset * -clk clock * -rom_addr Program Memory address * -rom_data Program Memory data * -ea external access: is used when the external memory is accesed * -int0 external interrupt 0 * -int1 external interrupt 1 * -dat_i input for external Data Memory * -dat_o output for external Data Memory * -adr_o external Data Memory address * -we_o writing to external Data Memory * -stb_o strobe * -ack_i acknowlage * -cyc_o cycle * -p0_in, p1_in, p2_in, p3_in port inputs * -p0_out, p1_out, p2_out, p3_out port outputs * -op1, op2, op3 inputs from external Program Memory (3x 8 bitov) * -rxd receive * -txd transmit * -t0, t1 t/c external inputs ===2.2 oc8051_decoder=== oc8051_decoder是主要模块。 该模块从程序存储器中获取操作代码,然后设置控制信号。 \\ 模块有两个内部信号。 \\ 首先是两位宽的信号-state。 该信号显然保存有关状态的信息,即流水线的状态。 该信号的典型值为b00。 仅当流水线中的指令执行被阻塞时(遇到jumps),才会更改此值。 \\ 第二个内部信号是op,这是寄存器,我们在其中保存操作代码,并且当指令执行需要多于一个时钟周期时,op才会被用到。\\ 这个模块由5个‘always’块组成:\\ * 第一个利用'case'判断流水线状态,通过状态state和操作码op设置控制信号。 * 第二个是用来记住操作码。 * 第三个是设置信号state到期望值。 * 第四个关注输出信号中断返回指令 * 第五个用来产生信号write_x 输入输出信号\\ - clk (in) clock \\ - rst (in) reset\\ - op_in (in) 操作码, 由memory_interface得到,[oc8051_op_select.op1]\\ - eq (in) 比较结果 [oc8051_comp.eq]\\ - ram_rd_sel (out) 选择信号, 读ram地址选择 [oc8051_ram_rd_sel.sel, oc8051_sp.ram_rd_sel]\\ - ram_wr_sel (out) 选择信号,写ram地址选择 [oc8051_ram_wr_sel.sel -r, oc8051_sp.ram_wr_sel -r]\\ - wr (out) 读写位1表示写,0表示读[oc8051_ram_top.wr -r, oc8051_acc.wr -r, oc8051_b_register.wr -r, oc8051_sp.wr-r, oc8051_dptr.wr -r, oc8051_psw.wr -r,oc8051_indi_addr.wr -r, oc8051_ports.wr -r]\\ - src_sel1 (out) 选择alu 资源1 [oc8051_alu_src1_sel.sel -r]\\ - src_sel2 (out) 选择alu 资源 2 [oc8051_alu_src2_sel.sel -r]\\ - src_sel3 (out) 选择alu 资源 3 [oc8051_alu_src3_sel.sel -r]\\ - alu_op (out) alu 操作[oc8051_alu.op_code -r]\\ - psw_set (out) 定义在PSW寄存器中设置哪个标志 cy, ac, ov from alu [oc8051_psw.set -r]\\ - cy_sel (out) alu中进位选择信号 [oc8051_cy_select.cy_sel -r]\\ - comp_sel (out) 比较内容选择 [oc8051_comp.sel]\\ - bit_addr (out) 位寻址 [oc8051_ram_top.bit_addr -r, oc8051_acc.wr_bit -r, oc8051_b_register.wr_bit-r, oc8051_sp.wr_bit -r, oc8051_dptr.wr_bit -r, oc8051_psw.wr_bit -r, oc8051_indi_addr.wr_bit -r, oc8051_ports.wr_bit -r]\\ - pc_wr (out) pc 写 [oc8051_pc.wr]\\ - pc_sel (out) pc 选择[oc8051_pc.pc_wr_sel]\\ - rd (out) rom 中读[oc8051_pc.rd, oc8051_op_select.rd]\\ - reti (out) 中断返回 [pin]\\ - rmw (out) 读-修改-写 指令时有效[oc8051_ports.rmw]\\ - pc_wait (out)\\ ===2.3 oc8051_alu=== 模块oc8051_alu表示用于算术和逻辑运算的组合逻辑。\\ 模块有3个8位输入操作数(第三个操作数用于计算PC或DPTR的地址)和3个输入信号。 这三个信号分别是进位,辅助进位和用于位寻址指令的信号。还有四位宽的操作码输入。\\ 操作指令:\\ * OC8051_ALU_NOP – 空操作 * OC8051_ALU_ADD - 加 * OC8051_ALU_SUB - 减 * OC8051_ALU_MUL - 乘 * OC8051_ALU_DIV - 除 * OC8051_ALU_DA – 十进制调整 * OC8051_ALU_NOT – 取反, 按位取反 * OC8051_ALU_AND – 与, 按位与 * OC8051_ALU_XOR – 异或 * OC8051_ALU_OR - 或 * OC8051_ALU_RL – 循环左移 * OC8051_ALU_RLC – 带进位的循环左移 * OC8051_ALU_RR – 循环右移 * OC8051_ALU_RRC – 带进位的循环右移 * OC8051_ALU_PCS – 添加16位无符号数和8位有符号数 * OC8051_ALU_XCH – 交换,第一个输入传输到第二个输出,反之亦然。 如果设置了进位,则只更改字节的最低半部分 运算码在文件oc8051_defines.v中。输出是2个八位宽结果,还有进位,辅助进位和溢出。乘法和除法子模块分别定义在oc8051_multiply 和 oc8051_divide 中。它俩都有八位宽的输入总线和2个八位宽输出总线,1个总线输出结果,另一个总线输出进位标志。 ===2.4 oc8051_pc=== 模块oc8051_pc实际上是一个程序计数器。 它计算下一条指令的地址值。模块中的输入是操作码,我们用它来计算地址的值。 还有一些输入用于跳转(op2和op3用于绝对跳转,alu输入用于相对寻址),还有用于选择新pc源的信号(pc_wr_sel)和输入新地址的信号(wr)。 模块的唯一输出是程序计数器的16位宽的当前值。 ===2.5 oc8051_rom=== 该模块包含了程序存储器。 程序存储器取决于我们的实现方式。\\ 模块中的输入是16位地址。 输出是三个8位数据总线和ea_int信号。 输入地址来自数据的第一个字节(data1),第二个和第三个字节(data2,data3)位于以下地址。 这是流水线不间断运行的必备条件。Ea_int信号等于外部ea信号,如果所使用的地址对于内部程序存储器来说太大了,那么就需要使能该信号,来访问外部程序存储器。\\ ===2.6 oc8051_comp=== 模块oc8051_comp的功能是比较两个输入并在输入相同时设置输出。 在条件跳转时,计算条件需要该模块。\\ 比较输入有不同的选项:\\ - ACC对0 \\ - 算术运算的结果对0 \\ - 进位 \\ - 位进位(来自存储器) \\ 这些选项足以满足8051中的所有条件跳转。输出连接到oc8051_decoder的输入,在需要时传输到pc_wr。\\ ===2.7 oc8051_op_select=== 所有来自程序存储器的数据都要经过该模块。它有三项任务。\\ 第一项任务是选择使用哪种内存,内部或外部。这项任务有点像多路复用器:它具有用于输入的ea和ea_int信号(当这两个信号任意一个低电位时,将是外部空间的读周期)和输出。\\ 该模块的第二个任务是接收中断。为此,除了3个8位输入之外,该模块还具有两个用于接收中断的输入信号。这些是信号int和8位的int_v。如果设置了int信号,我们有一个中断,在8位总线上,我们接收到中断程序的地址(高8位为零)。在中断时,检查当前正在执行的指令是否大于一个时钟周期(输入信号rd),然后将LCALL操作码发送到第一个输出。随后发送中断程序其他两个输出的地址。\\ 该模块的最后一个任务是检查操作码并发送存储器地址,以便将结果写入输出中。这与需要DPTR用于计算结果的指令和使用B寄存器的指令一起使用。使用此选项可以实现以后的立即寻址模式。必须要小心啦,因为第二个操作数有两个不同输出,一个用于ALU中的立即操作数,另一个用于直接寻址。\\ ===2.8 oc8051_regX=== oc8051_regX模块代表X位寄存器,其功能仅在于将信号延迟一个时钟周期。 除了时钟和复位输入外,它们还具有数据输入和输出。 ====3 数据存储器和特殊功能寄存器(SFR)==== 下面的模块包括两部分, * 数据存储器和特殊功能寄存器模块 * 多路复用器模块 这部分的模块都包含数据,这些模块共享同样的地址空间,因此他们有一些共同的输入信号:\\ 这些信号有:\\ - clk clock,时钟信号\\ - rst reset. Reset values are written oc8051_defines.v file. 复位信号\\ - wr writing 写操作信号\\ - wr_addr address to where data is written 写地址信号\\ - data_in input data 输入数据\\ - wr_bit defines if the instruction is bit addressable 如果指令可操作位地址,则有该信号\\ - bit_in bit input (for use only with bit addressable instructions and Data Memory) 位输入 (只用在位操作指令和位操作数据空间)\\ 在下列情况下,特殊寄存器每时钟周期都要被检查:\\ 1、如果有写周期(wr信号);\\ 2、如果位操作;\\ 3、如果地址匹配要写入寄存器的数据。\\ 地址可以不同,以便处理位操作指令或字节操作指令。物理地址定义在oc8051_defines.v文件中。\\ 除了输入空间外,还有数据地址输入空间和数据输出空间。\\ ===3.1 oc8051_ram_top=== 该模块包含数据存储器。 它的工作方式类似于普通内存和我们所需的(能够进行位寻址)的内存类型。 在位寻址模式中,我们必须使用地址字节中正确的位数值。 当写周期时,必须读取整个字节,改变相应位的值,必须将所有字节再写回存储器。\\ 该模块的子模块是oc8051_ram。 该模块依赖于所用的实现方式,它是一个具有8位地址的普通存储器。 由于流水线的原因,需要同时进行读写。\\ ===3.2 oc8051_acc=== 最常用的SFR是累加器(ACC)。 除了标准端口之外,它还有8位宽的输入,用于给第二个ALU结果(data2_in)。还有一个信号wad2,当第二结果写入寄存器时,该信号被激活。 还有另一个输出,用于奇偶校验(p)。 ===3.3 oc8051_b_register=== B寄存器是简单的位寻址寄存器没有特殊功能 ===3.4 oc8051_psw=== 这个模块包含程序状态字。除了标准输入外,它还有 来自ACC的信号p(奇偶);\\ 来自ALU的辅助进位和溢出信号;\\ 置位要写入寄存器的对应信号\\ {{::oc8051-psw.png|}}\\ ===3.5 oc8051_dptr=== 该模块包含16位宽数据指针。它有以下内容:\\ * 两个8位输出(data_hi和data_lo) * 8位输入总线,用于第二个ALU结果 * 2位宽的信号,当指令需要使用DPTR作为十六位宽寄存器时,该信号被激活 该寄存器不可位寻址。 ===3.6 oc8051_sp=== 该模块表示堆栈指针。 除了标准输入外,它还有两个连接到oc8051_decoder的输入信号。 这两个输入信号定义了读或者写的地址。 ===3.7 oc8051_ports=== 该模块负责输入输出端口。 \\ 它有四个8位输入总线和四个8位输出总线。 该信号用于与外界通信。 模块输入也是8位当前地址。 该模块还具有rmw信号,该信号指示该指令是否是所谓的读 - 修改 - 写指令。 根据这些指令,我们不读取模块的输入引脚,而是读取输出端口的寄存器。\\ 这些指令是: - ANL \\ - ORL \\ - XRL \\ - JBC \\ - CPL \\ - INC \\ - DEC \\ - DJNZ \\ - MOV PX.Y, C \\ - CLR PX.Y \\ - SETB PX.Y \\ ===3.8 oc8051_tc=== 该模块描述oc8051定时器。有两个定时器:定时器/计数器0(T0/C0)和定时器/计数器1(T1/C1)。两个定时器都是16位字长,每个定时器由两个8位寄存器表示(T/C 0为TL0和TH0,T/C1为TL1和TH1)。该模块还包含SFR TMOD,它定义了定时器模式。\\ {{::oc8051-tmod.png|}}\\ 四个输入信号是ie0,ie1,tr0,tr1,表示激活定时器的状态。另外还有两个输出信号tf0和tf1,这两个信号用来设置TCON寄存器和八位输出总线的溢出标志。\\ 定时器有四种不同的运行模式:\\ * 模式0:两个定时器都是带有32分频预分频器的8位计数器,它提供了一个13位计数器。 仅使用低5位的TLx寄存器。 * 模式1:两个寄存器都是16位计数器 * 模式2:THx表示8位计数器,溢出时填充TLx内容 * 模式3:在这种模式下,t / c1只保持恒定值。 而t / c0用作两个独立的8位计数器。 TH0使用来自timer0的控制信号(TF0中的TR0),而TL0使用来自timer1的控制信号(TF1中的TR1)。 ===3.9 oc8051_int=== 中断模块。该模块接收中断请求,然后根据已定义的状态,调度请求到处理器中。这里定义了五种不同中断源,每一种的中断函数都有自己的独一的地址。\\ These addresses are: * external interrupt 0 (0003H) * timer 0 overflow (000BH) * external interrupt 1 (0013H) * timer 1 overflow (001BH) * serial port interrupt (0023H) 该模块包含三个特殊功能寄存器:\\ * 控制寄存器 (TCON), 包含中断标志\\ {{::oc8051-tcon.png|}}\\ * 中断使能寄存器(IE) {{::oc8051-ie.png|}}\\ * 中断优先级寄存器(IP) {{::oc8051-ip.png|}}\\ 模块有五个中断输入,每个对应一个中断源。 还有两个输入信号,reti和ack。中断结束时reti置位,当处理器打断向量中断任务时,信号ack置位。 模块还具有8位总线,用于读取中断向量地址。\\ ===3.10 oc8051_uart=== 该模块包含oc8051串行接口(uart)。 除标准输入外,它还具有接收输入信号(rxd)和发送输入信号(txd)。 这两个信号也是处理器的输出。 还有一个timer1溢出输入和一个中断输出。模块包含三个SFR:串行控制(scon),串行数据缓冲器(sbuf)和功率控制(pcon)。 串口控制寄存器 (SCON)\\ {{::oc8051-scon.png|}}\\ 串口有四种操作模式:\\ * 模式0:传输8个数据位。 波特率是振荡器频率的1/12。 * 模式1:传输10位(8个数据位和启动和停止位)。 波特率是可变的。 * 模式2:传输11位:起始位,8个数据位,可编程的第9位和停止位。 如果设置了smod,则波特率为1/32,否则为波特率的1/64。 * 模式3:传输11位:起始位,8个数据位,可编程的第9位和停止位。 波特率是可变的。 当处于模式1或3时,需要timer1来计算波特率(波特率=(2 ^ smod / 64)*(定时器1溢出率))。 ===3.11 oc8051_indi_addr=== 该模块不包含任何SFR,但它仍包含数据存储器的一部分。 来自所有寄存器组R0和R1的数据。 该寄存器用于间接寻址。输入在特定的寄存器缓冲区占据两位bit,操作码的最后一部分用来选择寄存器R0或R1。因此在直接寻址中,在第一时钟阶段,操作数已经就位,没有必要停止流水线。 ===3.12 oc8051_ram_sel=== 该模块代表多路复用器,基于该复用器,读入的指针能够将正确的数据传给数据总线。这样就可以选择存储器中的数据或者特殊寄存器中的数据。 ====4 多路复用器==== 总线管理是基于多路复用器的,他们的主要特点是选择输入信号并转换成相应的输出信号。输入信号来自oc8051_decoder (译码)模块的八位宽的信号。 ===4.1 oc8051_alu_src1_sel=== 这个模块用来选择第一个ALU操作数。他们来自立即操作数,ACC,有效的内部空间数据或外部空间数据。 //根据src3更改 ===4.2 oc8051_alu_src2_sel=== 这个模块用来选择第二个ALU操作数。他们来自立即操作数,ACC,有效的内部空间数据或外部空间数据或者零。 ===4.3 oc8051_alu_src3_sel=== 这个模块用来选择第三个ALU操作数。他们来自程序计数器或者DPTR。 ===4.4 oc8051_cy_select=== 选择把以下哪里的进位送给ALU,\\ (1)来自PSW \\ (2)来自空间的位数据 \\ (3)0 \\ (4)1 \\ ===4.5 oc8051_ext_addr_sel=== 选择外部空间地址:R0或R1 (等同于间接寻址) 或者 DPTR。 ===4.6 oc8051_immediate_sel=== 选择立即操作数。有两个输出,一个用作第一个ALU操作数,另一个用作第二个ALU操作数。可以在PC (程序计数器),第二个或第三个指令字节之中选择。 ===4.7 oc8051_ram_rd_sel=== 选择读地址: (1)寄存器(R0-R7);\\ (2)间接地址;\\ (3)堆栈;\\ (4)直接地址。\\ 当选择地址寄存器时,只有五个位的数值被使用(前三个位的数值总是零)。 ===4.8 oc8051_ram_wr_sel=== 选择写地址: (1)寄存器(R0-R7); (2)间接地址; (3)堆栈; (4)直接地址; (5)DPTR; (6)B寄存器。 ===4.9 oc8051_rom_addr_sel=== 选择程序空间地址: (1)PC; (2)DPRT (只在MOVC指令下有效)。 \\ ===参考来源===: \\ opencores的设计文档 < oc8051_design > \\ 李全利老师主编的《单片机原理及接口技术》 \\ 网友oldbeginner的开源软核学习笔记:http://xilinx.eetop.cn/space.php?uid=1214938&op=bbs \\ 网友leishangwen的《DE2上使用OC8051运行点灯程序》: \\ https://download.csdn.net/download/leishangwen/5173363 \\ 由于水平所限,难免有不对之处,欢迎指正。 \\