完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
本帖最后由 dongyumin 于 2013-8-12 19:31 编辑 引言 经过对OpenRISC近一年的分析与研究,在此过程中我们了解了计算机体系结构设计相关的主要概念,重要的技术,和基本思想。我觉的,现在我们有必要练练手了。 本小节,我们将设计一个简单的cpu,包括ISA的设计,模块的划分,RTL实现,编写asm汇编程序,用modelsim进行仿真,以及用quartusII的综合。 1,计算器与计算机 我认为,在EDVAC计算机之前的计算机,都可认为是计算器。 原因是,冯诺依曼对EDVAC分析时提出了二进制运算和加入存储部件,而在这之前的计算机是没有存储功能的,比如我们要计算(1+2)&(3+4),如果是采用计算器,其运算步骤如下: a,先用计算器算出1+2的结果3,然后人脑自己记住这个数。 b,再用计算器计算出3+4的结果7,人脑也自己记住这个数。 c,最后用计算器算出3&7的结果3。 如果采用计算机,其运算过程如下: 首先我们需要写一段程序,假设程序放在程序存储器的地址0x0处,数据1,2,3,4分别放在数据存储器的55,56,57,58四个地址。 程序的执行过程如下: a,将data_mem的0x55处的数据放到r1。 b,将data_mem的0x56处的数据放到r2。 c,执行add r2,r1,结果放在r2里面。 d,将r2的内容写入到data_mem的0x60这个地址。 e,将data_mem的0x57处的数据放到r3。 f,将data_mem的0x58处的数据放到r4。 g,执行add r4,r3,结果放在r4里面。 h,将r4的内容写入到data_mem的0x61这个地址。 i,将data_mem的0x60处的数据放到r5。 j,将data_mem的0x61处的数据放到r6。 k,执行and r6,r5,结果放在r6里面。 l,将r6的内容写入到data_mem的0x62这个地址,最终得到计算结果。 我们可以看出,如果用计算器计算,只需三步就可以完成,但是如果用计算机的话需要12步才能完成。那是不是用计算机的效率低呢?今天计算机的蓬勃发展使答案不言而喻。 原因就是只要实现写好程序,用计算机的整个计算过程不用人为干预。 我想这正是计算机发展的根本原因之所在,就是计算机的出现是对人的很大解放。我们只要按照一定的方式写好程序,然后交给计算机,计算机会自动完成任务,而我们的手就可以干些其他的事情了! 2,架构设计 1>整体设计 通过上面的例子,我们可以体会到计算机的好处,下面我们就动手设计一个cpu,来完成1+2的计算。 关于计算机体系结构,我们之前说过的内容已经够多了。这里只说明一下几点: a,我们采用harvard结构,即,指令和数据的总线是独立的。 b,流水线,我们暂时不采用流水设计,但是在最后,我给出了五级流水时的数据通路设计框架。 c,关于指令集,由于是学习目的,我们只是先基本的访存指令,运算指令和分支指令。运算不支持乘除和浮点。关于具体的指令细节,请参考下面的RTL代码即可。 d,为了对我们设计的cpu进行仿真和验证,我们需要设计一个简单的soc才行,这个soc只包含指令存储器,cpu内核,数据存储器。 下面是soc的整体架构图:我们给他取个名字吧,就叫 tiny_soc,小cpu就简单的称她为tiny_core 。 2>模块划分 cpu core的结构如下: 正个cpu core由数据通路和控制通路和调试单元组成。 其中数据通路包括: PC的产生模块:genpc 运算模块:alu,在alu的前面是对操作数多选一的一个mux。 寄存器堆:regfile模块 还有栈:stack。 数据通路受控制通路模块ctrl_path模块的控制,控制通路负责指令的解码,并产生对应的控制信号。 调试单元,由于只是学习目的,调试单元最简化,只输出当前的PC值和当前的指令内容两个信息。 3,模块划分与接口定义 整体的架构设计完成后,我们就需要进一步的细化了,这时,需要定义目的的模块名称,模块功能,一旦功能确定之后我们就可以确定具体的模块接口信号了。 如果模块功能过大,我们需要拆分成更小的模块,这就是top-down的设计方法。关于设计方法学(top-down,bottom-up),很多资料里都有介绍,这里就不再赘述了。 一个完整的工程,和做理论研究不同,需要处理很多实现细节,下面,我们介绍一下,其中比较重要的部分: 1>genpc模块 这里面需要考虑三点:上电复位PC默认值为多少?正常指令执行时PC如何变化?遇到分支指令时PC如何变化? 关于上电默认值,我们可以通过一个define语句来设定,允许用户后期修改方便。 关于正常指令的指令,PC是加1还是加2还是加4,这要看指令存储器的访问方式,我们的指令存储器是每个地址放一条指令,每条指令是2个字节(16-bit),所以我们只要PC加1就可以了。 关于遇到分支指令,我们直接将控制通路经过解码的跳转地址赋给PC即可。 genpc模块的C语言伪代码如下: [html] view plaincopyprint? 1 genpc module pseudo code 2 3 if(rst) 4 { 5 pc= boot_addr; 6 } 7 else 8 { 9 if(branch) 10 { 11 pc = branch_target; 12 } 13 else 14 { 15 pc = pc +1; 16 } 17 } 2>alu模块 alu模块,大家都很熟悉了,是执行单元部件,负责运算指令的运算工作。 这个模块的输入信号是有控制通路的解码出来的操作数和操作码,输出信号就是运算结果。 需要说明的是,这个模块可以完全是组合逻辑电路。 3>rf模块 register file模块,从物理上来说,就是一个block ram。 从逻辑上来说,这个模块是对软件程序员是透明的,寄存器堆和指令集是软件和硬件的交互接口。 4>stack stack(栈),是用来处理分支指令时,存放PC的值的,比如,我们在处理子程序调用时,需要先将当前的PC+1压栈,等到遇到子程序返回指令时使用。 栈的特点是LIFO(last in first out),这一点与heap(堆)不同。 5>ctrl_path模块 控制通路负责将genpc模块产生的地址处的指令进行解码,并产生对应的操作数,操作码和控制型号。这部分信号比较多一点。 6>tiny_soc 为了测试这个cpu内核,我们需要搭一个最小系统,包括指令只读存储器insn_rom模块,里面存放机器码。 由于是harvard结构,所以还需要一个数据存储器ram模块,相当于内存。 当然,如果想外挂其他I/O外设,我们只需要定义其地址空间即可,需要说明的是I/O外设的地址空间不能与RAM重叠,各个I/O外设之间也不能重叠。 RAM和I/O外设之间可通过一个arbiter与cpu core实现数据交互。 当然,如果存放指令的地方不止一个,也需要一个指令arbiter。 4,RTL实现 在完成模块划分,接口定义,仔细分析考虑模块间时序之后,如果没有什么问题,我们就可以进行编码工作了。 编码,需要注意的是编码一定要规范,信号命名,代码注释什么的,尽量要仔细。这里直接给出RTL代码(verilog HDL) 按照自上而下的顺序依次给出: 1>tiny_soc顶层模块:soc_top [html] view plaincopyprint? 18 /* 19 * 20 * file name : soc_top.v 21 * author : Rill 22 * date : 2013-08-11 23 * 24 */ 25 26 `timescale 1ns / 1ps 27 28 module soc_top 29 ( 30 input clk, 31 input rst 32 ); 33 34 35 wire read_e; 36 wire write_e; 37 wire [7:0] port_addr; 38 wire [7:0] core_in; 39 wire [7:0] core_out; 40 wire [15:0] instruction; 41 wire [10:0] inst_addr; 42 43 wire [15:0] debug_insn; 44 wire [10:0] debug_pc; 45 46 insn_rom insn_rom 47 ( 48 .clk (clk), 49 .rst (rst), 50 .address (inst_addr), 51 .instruction (instruction) 52 ); 53 54 core core 55 ( 56 .clk (clk), 57 .rst (rst), 58 59 .read_e (read_e), 60 .write_e (write_e), 61 .port_addr (port_addr), 62 .data_in (core_in), 63 .data_out (core_out), 64 .inst_addr (inst_addr), 65 .instruction (instruction), 66 .debug_pc (debug_pc), 67 .debug_insn (debug_insn) 68 ); 69 70 ram ram 71 ( 72 .clk (clk), 73 .rst (rst), 74 75 .wr (write_e), 76 .rd (read_e), 77 .addr (port_addr), 78 .din (core_out), 79 .dout(core_in) 80 ); 81 82 83 84 endmodule 2>指令存储器:insn_rom [html] view plaincopyprint? 85 /* 86 * 87 * file name : insn_rom.v 88 * author : Rill 89 * date : 2013-08-11 90 * 91 */ 92 93 94 95 module insn_rom 96 ( 97 input clk, 98 input rst, 99 input [10:0] address, 100 output reg [15:0] instruction 101 ); 102 103 //(* RAM_STYLE="BLOCK" *) 104 reg [15:0] rom [2047:0]; 105 106 always @(posedge clk) 107 begin 108 if(rst) 109 begin 110 rom[0] <= 16'h5801;//0: jmp start 111 rom[1] <= 16'h1101;//1:start mov r1,1 112 rom[2] <= 16'h1202;//2: mov r2,2 113 rom[3] <= 16'h3220;//3: add r2,r1 114 rom[4] <= 16'h2237;//4: str r2,55 115 rom[5] <= 16'h5806;//5: jmp end 116 rom[6] <= 16'h5806;//6:end jmp end*/ 117 end 118 else 119 begin 120 instruction <= rom[address]; 121 end 122 end 123 124 125 endmodule 3>数据存储器:ram [html] view plaincopyprint? 126 * 127 * 128 * file name : ram.v 129 * author : Rill 130 * date : 2013-08-11 131 * 132 */ 133 134 135 module ram( 136 input clk, 137 input rst, 138 139 input [7:0] din, 140 input [7:0] addr, 141 output reg [7:0] dout, 142 input wr, 143 input rd 144 ); 145 146 (* RAM_STYLE="DISTRIBUTED" *) 147 148 reg [7:0] ram [255:0]; 149 150 always @(posedge clk) 151 begin 152 if(rst) 153 begin 154 dout <= 8'b0; 155 ram[0] = 0; 156 ram[1] = 1; 157 ram[2] = 2; 158 ram[32] = 32; 159 ram[64] = 64; 160 161 end 162 else 163 begin 164 if (wr) 165 ram[addr] <= din; 166 else if(rd) 167 dout <= ram[addr]; 168 end 169 end 170 171 endmodule 4>CPU核心:core [html] view plaincopyprint? 172 /* 173 * 174 * file name : core.v 175 * author : Rill 176 * date : 2013-08-11 177 * 178 */ 179 180 181 182 module core 183 ( 184 input clk, 185 input rst, 186 output [7:0] port_addr, 187 output read_e, 188 output write_e, 189 input [7:0] data_in, 190 output [7:0] data_out, 191 output [10:0] inst_addr, 192 input [15:0] instruction, 193 194 output [10:0] debug_pc,//debug i/f 195 output [15:0] debug_insn 196 ); 197 198 wire z,c; 199 wire insel; 200 wire we; 201 wire [2:0] raa; 202 wire [2:0] rab; 203 wire [2:0] wa; 204 wire [2:0] opalu; 205 wire [2:0] sh; 206 wire selpc; 207 wire ldpc; 208 wire ldflag; 209 wire [10:0] ninst_addr; 210 wire selk; 211 wire [7:0] KTE; 212 wire [10:0] stack_addr; 213 wire wr_en, rd_en; 214 wire [7:0] imm; 215 wire selimm; 216 217 218 219 220 control_path control_path 221 ( 222 .clk (clk), 223 .rst (rst), 224 .instruction (instruction), 225 .z (z), 226 .c (c), 227 .port_addr (port_addr), 228 .write_e (write_e), 229 .read_e (read_e), 230 .insel (insel), 231 .we (we), 232 .raa (raa), 233 .rab (rab), 234 .wa (wa), 235 .opalu (opalu), 236 .sh (sh), 237 .selpc (selpc), 238 .ldpc (ldpc), 239 .ldflag (ldflag), 240 .naddress (ninst_addr), 241 .selk (selk), 242 .KTE (KTE), 243 .stack_addr (stack_addr), 244 .wr_en (wr_en), 245 .rd_en (rd_en), 246 .imm (imm), 247 .selimm (selimm) 248 ); 249 250 251 252 data_path data_path_i 253 ( 254 .clk (clk), 255 .rst (rst), 256 .data_in (data_in), 257 .insel (insel), 258 .we (we), 259 .raa (raa), 260 .rab (rab), 261 .wa (wa), 262 .opalu (opalu), 263 .sh (sh), 264 .selpc (selpc), 265 .selk (selk), 266 .ldpc (ldpc), 267 .ldflag (ldflag), 268 .wr_en (wr_en), 269 .rd_en (rd_en), 270 .ninst_addr (ninst_addr), 271 .kte (KTE), 272 .imm (imm), 273 .selimm (selimm), 274 .data_out (data_out), 275 .inst_addr (inst_addr), 276 .stack_addr (stack_addr), 277 .z (z), 278 .c (c) 279 ); 280 281 debug debug 282 ( 283 .pc_in (inst_addr), 284 .insn_in (instruction), 285 286 .pc (debug_pc), 287 .insn (debug_insn) 288 ); 289 290 291 endmodule 5>调试单元:debug [html] view plaincopyprint? 292 /* 293 * 294 * file name : debug.v 295 * author : Rill 296 * date : 2013-08-11 297 * 298 */ 299 300 301 302 module debug 303 ( 304 input [10:0] pc_in, 305 input [15:0] insn_in, 306 307 output [10:0] pc, 308 output [15:0] insn 309 ); 310 311 assign pc = pc_in; 312 assign insn = insn_in; 313 314 endmodule 6>控制通路:control_path [html] view plaincopyprint? 315 /* 316 * 317 * file name : control_path.v 318 * author : Rill 319 * date : 2013-08-11 320 * 321 */ 322 323 324 325 module control_path 326 ( 327 input clk, 328 input rst, 329 input [15:0] instruction, 330 input z, 331 input c, 332 output reg [7:0] port_addr, 333 output reg write_e, 334 output reg read_e, 335 output reg insel, 336 output reg we, 337 output reg [2:0] raa, 338 output reg [2:0] rab, 339 output reg [2:0] wa, 340 output reg [2:0] opalu, 341 output reg [2:0] sh, 342 output reg selpc, 343 output reg ldpc, 344 output reg ldflag, 345 output reg [10:0] naddress, 346 output reg selk, 347 output reg [7:0] KTE, 348 input [10:0] stack_addr, 349 output reg wr_en, rd_en, 350 output reg [7:0] imm, 351 output reg selimm 352 ); 353 354 355 parameter fetch= 5'd0; 356 parameter decode= 5'd1; 357 358 parameter ldi= 5'd2; 359 parameter ldm= 5'd3; 360 parameter stm= 5'd4; 361 parameter cmp= 5'd5; 362 parameter add= 5'd6; 363 parameter sub= 5'd7; 364 parameter andi= 5'd8; 365 parameter oor= 5'd9; 366 parameter xori= 5'd10; 367 parameter jmp= 5'd11; 368 parameter jpz= 5'd12; 369 parameter jnz= 5'd13; 370 parameter jpc= 5'd14; 371 parameter jnc= 5'd15; 372 parameter csr= 5'd16; 373 parameter ret= 5'd17; 374 375 parameter adi= 5'd18; 376 parameter csz= 5'd19; 377 parameter cnz= 5'd20; 378 parameter csc= 5'd21; 379 parameter cnc= 5'd22; 380 parameter sl0= 5'd23; 381 parameter sl1= 5'd24; 382 parameter sr0= 5'd25; 383 parameter sr1= 5'd26; 384 parameter rrl= 5'd27; 385 parameter rrr= 5'd28; 386 parameter noti= 5'd29; 387 388 parameter nop= 5'd30; 389 390 wire [4:0] opcode; 391 reg [4:0] state; 392 393 assign opcode=instruction[15:11]; 394 395 always@(posedge clk or posedge rst) 396 begin 397 if (rst) 398 begin 399 state<=decode; 400 end 401 402 else 403 begin 404 case (state) 405 fetch: 406 begin 407 state<=decode; 408 end 409 410 decode: 411 begin 412 if(opcode >=ldi && opcode <=nop) 413 state <= opcode;//state just is the opcode now 414 else 415 state <= nop; 416 end 417 418 default: 419 state<=fetch; 420 endcase 421 end 422 423 end 424 425 426 always@(*) 427 begin 428 port_addr<=0; 429 write_e<=0; 430 read_e<=0; 431 insel<=0; 432 we<=0; 433 raa<=0; 434 rab<=0; 435 wa<=0; 436 opalu<=4; 437 sh<=4; 438 selpc<=0; 439 ldpc<=1; 440 ldflag<=0; 441 naddress<=0; 442 selk<=0; 443 KTE<=0; 444 wr_en<=0; 445 rd_en<=0; 446 imm<=0; 447 selimm<=0; 448 449 case (state) 450 fetch: begin 451 ldpc<=0; 452 end 453 454 decode: begin 455 ldpc<=0; 456 if (opcode==stm) 457 begin 458 raa<=instruction[10:8]; 459 port_addr<=instruction[7:0]; 460 end 461 else if (opcode==ldm) 462 begin 463 wa<=instruction[10:8]; 464 port_addr<=instruction[7:0]; 465 end 466 else if (opcode==ret) 467 begin 468 rd_en<=1; 469 end 470 end 471 472 ldi: begin 473 selk<=1; 474 KTE<=instruction[7:0]; 475 we<=1; 476 wa<=instruction[10:8]; 477 end 478 479 ldm: begin 480 wa<=instruction[10:8]; 481 we<=1; 482 read_e<=1; 483 port_addr<=instruction[7:0]; 484 end 485 486 stm: begin 487 raa<=instruction[10:8]; 488 write_e<=1; 489 port_addr<=instruction[7:0]; 490 end 491 492 cmp: begin 493 ldflag<=1; 494 raa<=instruction[10:8]; 495 rab<=instruction[7:5]; 496 opalu<=6; 497 end 498 499 add: begin 500 raa<=instruction[10:8]; 501 rab<=instruction[7:5]; 502 wa<=instruction[10:8]; 503 insel<=1; 504 opalu<=5; 505 we<=1; 506 end 507 508 sub: begin 509 raa<=instruction[10:8]; 510 rab<=instruction[7:5]; 511 wa<=instruction[10:8]; 512 insel<=1; 513 opalu<=6; 514 we<=1; 515 end 516 517 andi: begin 518 raa<=instruction[10:8]; 519 rab<=instruction[7:5]; 520 wa<=instruction[10:8]; 521 insel<=1; 522 opalu<=1; 523 we<=1; 524 end 525 526 oor: begin 527 raa<=instruction[10:8]; 528 rab<=instruction[7:5]; 529 wa<=instruction[10:8]; 530 insel<=1; 531 opalu<=3; 532 we<=1; 533 end 534 535 xori: begin 536 raa<=instruction[10:8]; 537 rab<=instruction[7:5]; 538 wa<=instruction[10:8]; 539 insel<=1; 540 opalu<=2; 541 we<=1; 542 end 543 544 jmp: begin 545 naddress<=instruction[10:0]; 546 selpc<=1; 547 ldpc<=1; 548 end 549 550 jpz: if (z) 551 begin 552 naddress<=instruction[10:0]; 553 selpc<=1; 554 ldpc<=1; 555 end 556 557 jnz: if (!z) 558 begin 559 naddress<=instruction[10:0]; 560 selpc<=1; 561 ldpc<=1; 562 end 563 564 565 jpc: if (c) 566 begin 567 naddress<=instruction[10:0]; 568 selpc<=1; 569 ldpc<=1; 570 end 571 572 573 jnc: if (!c) 574 begin 575 naddress<=instruction[10:0]; 576 selpc<=1; 577 ldpc<=1; 578 end 579 580 csr: begin 581 naddress<=instruction[10:0]; 582 selpc<=1; 583 ldpc<=1; 584 wr_en<=1; 585 end 586 587 ret: begin 588 naddress<=stack_addr; 589 selpc<=1; 590 ldpc<=1; 591 end 592 593 adi: begin 594 raa<=instruction[10:8]; 595 wa<=instruction[10:8]; 596 imm<=instruction[7:0]; 597 selimm<=1; 598 insel<=1; 599 opalu<=5; 600 we<=1; 601 end 602 603 csz: if (z) 604 begin 605 naddress<=instruction[10:0]; 606 selpc<=1; 607 ldpc<=1; 608 wr_en<=1; 609 end 610 611 cnz: if (!z) 612 begin 613 naddress<=instruction[10:0]; 614 selpc<=1; 615 ldpc<=1; 616 wr_en<=1; 617 end 618 619 csc: if (c) 620 begin 621 naddress<=instruction[10:0]; 622 selpc<=1; 623 ldpc<=1; 624 wr_en<=1; 625 end 626 627 cnc: if (!c) 628 begin 629 naddress<=instruction[10:0]; 630 selpc<=1; 631 ldpc<=1; 632 wr_en<=1; 633 end 634 635 sl0: begin 636 raa<=instruction[10:8]; 637 wa<=instruction[10:8]; 638 insel<=1; 639 sh<=0; 640 we<=1; 641 end 642 643 sl1: begin 644 raa<=instruction[10:8]; 645 wa<=instruction[10:8]; 646 insel<=1; 647 sh<=5; 648 we<=1; 649 end 650 651 sr0: begin 652 raa<=instruction[10:8]; 653 wa<=instruction[10:8]; 654 insel<=1; 655 sh<=2; 656 we<=1; 657 end 658 659 sr1: begin 660 raa<=instruction[10:8]; 661 wa<=instruction[10:8]; 662 insel<=1; 663 sh<=6; 664 we<=1; 665 end 666 667 rrl: begin 668 raa<=instruction[10:8]; 669 wa<=instruction[10:8]; 670 insel<=1; 671 sh<=1; 672 we<=1; 673 end 674 675 rrr: begin 676 raa<=instruction[10:8]; 677 wa<=instruction[10:8]; 678 insel<=1; 679 sh<=3; 680 we<=1; 681 end 682 683 noti: begin 684 raa<=instruction[10:8]; 685 wa<=instruction[10:8]; 686 insel<=1; 687 opalu<=0; 688 we<=1; 689 end 690 691 nop: begin 692 opalu<=4; 693 end 694 endcase 695 end 696 697 698 endmodule 7>数据通路:data_path [html] view plaincopyprint? 699 /* 700 * 701 * file name : data_path.v 702 * author : Rill 703 * date : 2013-08-11 704 * 705 */ 706 707 708 709 module data_path 710 ( 711 input clk, 712 input rst, 713 input [7:0] data_in, 714 input insel, 715 input we, 716 input [2:0] raa, 717 input [2:0] rab, 718 input [2:0] wa, 719 input [2:0] opalu, 720 input [2:0] sh, 721 input selpc, 722 input selk, 723 input ldpc, 724 input ldflag, 725 input wr_en, rd_en, 726 input [10:0] ninst_addr, 727 input [7:0] kte, 728 input [7:0] imm, 729 input selimm, 730 output [7:0] data_out, 731 output [10:0] inst_addr, 732 output [10:0] stack_addr, 733 output z,c 734 ); 735 736 wire [7:0] regmux, muximm; 737 wire [7:0] portA, portB; 738 739 wire [7:0] shiftout; 740 741 assign data_out=shiftout; 742 743 744 genpc genpc 745 ( 746 .clk (clk), 747 .rst (rst), 748 749 .ldpc (ldpc), 750 .selpc (selpc), 751 .ninst_addr (ninst_addr), 752 753 .inst_addr (inst_addr) 754 ); 755 756 757 alu_mux alu_mux 758 ( 759 .selimm (selimm), 760 .imm (imm), 761 .portB (portB), 762 763 .muximm (muximm) 764 ); 765 766 alu alu 767 ( 768 .a (portA), 769 .b (muximm), 770 .opalu (opalu), 771 .ldflag (ldflag), 772 .zero (z), 773 .carry (c), 774 .sh (sh), 775 .dshift (shiftout) 776 ); 777 778 779 stack stack 780 ( 781 .clk (clk), 782 .rst (rst), 783 .wr_en (wr_en), 784 .rd_en (rd_en), 785 .din (inst_addr), 786 .dout (stack_addr) 787 ); 788 789 790 regfile_mux regfile_mux 791 ( 792 .insel (insel), 793 .selk (selk), 794 .shiftout (shiftout), 795 .kte (kte), 796 .data_in (data_in), 797 798 .regmux (regmux) 799 ); 800 801 regfile regfile 802 ( 803 .datain (regmux), 804 .clk (clk), 805 .we (we), 806 .wa (wa), 807 .raa (raa), 808 .rab (rab), 809 .porta (portA), 810 .portb (portB) 811 ); 812 813 814 815 endmodule 8>程序计算器:genpc [html] view plaincopyprint? 816 /* 817 * 818 * file name : genpc.v 819 * author : Rill 820 * date : 2013-08-11 821 * 822 */ 823 824 `define boot_addr 0 //boot address after power on 825 826 module genpc 827 ( 828 input clk, 829 input rst, 830 831 input ldpc, 832 input selpc, 833 input [10:0] ninst_addr, 834 835 output [10:0] inst_addr 836 ); 837 838 reg [10:0] pc; 839 840 assign inst_addr=pc; 841 842 always@(posedge clk or posedge rst) 843 begin 844 if (rst) 845 pc <=`boot_addr; 846 else 847 if (ldpc) 848 if(selpc) 849 pc<=ninst_addr; 850 else 851 pc<=pc+1; 852 end 853 854 855 endmodule 9>运算单元:alu ,alu_mux [html] view plaincopyprint? 856 /* 857 * 858 * file name : alu_mux.v 859 * author : Rill 860 * date : 2013-08-11 861 * 862 */ 863 864 865 866 module alu_mux 867 ( 868 input selimm, 869 input [7:0] imm, 870 input [7:0] portB, 871 872 output [7:0] muximm 873 ); 874 875 assign muximm = selimm? imm : portB;//result : imm if ldi insn,portb if ldm insn 876 877 878 endmodule [html] view plaincopyprint? 879 /* 880 * 881 * file name : alu.v 882 * author : Rill 883 * date : 2013-08-11 884 * 885 */ 886 887 888 889 module alu 890 ( 891 input [7:0] a, 892 input [7:0] b, 893 input [2:0] opalu, 894 input ldflag, 895 output zero, 896 output carry, 897 input [2:0] sh, 898 output reg [7:0] dshift 899 ); 900 901 reg [7:0] resu; 902 903 assign zero=ldflag?(resu==0):1'b0; 904 905 assign carry=ldflag?(a 906 907 always@(*) 908 case (opalu) 909 0: resu <= ~a; 910 1: resu <= a & b; 911 2: resu <= a ^ b; 912 3: resu <= a | b; 913 4: resu <= a; 914 5: resu <= a + b; 915 6: resu <= a - b; 916 default: resu <= a + 1; 917 endcase 918 919 920 always@* 921 case (sh) 922 0: dshift <= {resu[6:0], 1'b0}; 923 1: dshift <= {resu[6:0], resu[7]}; 924 2: dshift <= {1'b0, resu[7:1]}; 925 3: dshift <= {resu[0], resu[7:1]}; 926 4: dshift <= resu; 927 5: dshift <= {resu[6:0], 1'b1}; 928 6: dshift <= {1'b1, resu[7:1]}; 929 default: dshift <= resu; 930 endcase 931 932 endmodule 10>寄存器堆:regfile,regfile_mux [html] view plaincopyprint? 933 /* 934 * 935 * file name : regfile_mux.v 936 * author : Rill 937 * date : 2013-08-11 938 * 939 */ 940 941 942 943 module regfile_mux 944 ( 945 input insel, 946 input selk, 947 input [7:0] shiftout, 948 input [7:0] kte, 949 input [7:0] data_in, 950 951 output [7:0] regmux 952 ); 953 954 wire [7:0] muxkte; 955 956 assign regmux=insel? shiftout : muxkte; 957 assign muxkte=selk? kte : data_in; 958 959 960 endmodule [html] view plaincopyprint? 961 /* 962 * 963 * file name : regfile.v 964 * author : Rill 965 * date : 2013-08-11 966 * 967 */ 968 969 970 971 module regfile( 972 input [7:0] datain, 973 input clk, we, 974 input [2:0] wa, 975 input [2:0] raa, 976 input [2:0] rab, 977 output [7:0] porta, 978 output [7:0] portb 979 ); 980 981 982 reg [7:0] mem [7:0];//r0 ~r255 983 984 always@(posedge clk) 985 begin 986 mem[0]<=0;//r0 always is 0 987 988 if(we) 989 mem[wa]<=datain; 990 end 991 992 assign porta=mem[raa]; 993 assign portb=mem[rab]; 994 995 996 endmodule 11>栈:stack [html] view plaincopyprint? 997 /* 998 * 999 * file name : stack.v 1000 * author : Rill 1001 * date : 2013-08-11 1002 * 1003 */ 1004 1005 1006 1007 module stack( 1008 input clk, 1009 input rst, 1010 input wr_en, 1011 input rd_en, 1012 input [10:0] din, 1013 output [10:0] dout 1014 ); 1015 1016 1017 (* RAM_STYLE="DISTRIBUTED" *) 1018 reg [3:0] addr; 1019 reg [10:0] ram [15:0]; 1020 1021 assign dout = ram[addr] +1; 1022 1023 always@(posedge clk) 1024 begin 1025 if (rst) 1026 addr<=0; 1027 else 1028 begin 1029 if (wr_en==0 && rd_en==1) //leer 1030 if (addr>0) 1031 addr<=addr-1; 1032 if (wr_en==1 && rd_en==0) //guardar 1033 if (addr<15) 1034 addr<=addr+1; 1035 end 1036 end 1037 1038 always @(posedge clk) 1039 begin 1040 if (wr_en) 1041 ram[addr] <= din; 1042 end 1043 1044 endmodule 5,modelsim仿真 1>编写testbench 要进行仿真,需要编写对应的testbench,由于咱们这个cpu很简单,所以测试激励也很简单,代码如下: [html] view plaincopyprint? 1045 /* 1046 * 1047 * file name : tiny_soc_tb.v 1048 * atthor : Rill 1049 * date : 2013-08-11 1050 * 1051 */ 1052 1053 `timescale 1ns / 1ps 1054 1055 module tiny_soc_tb; 1056 1057 1058 reg clk; 1059 reg rst; 1060 1061 1062 always #5 clk = ~clk; 1063 1064 initial 1065 begin 1066 #0 1067 clk = 0; 1068 rst = 0; 1069 #15 1070 rst = 1; 1071 #10 1072 rst = 0; 1073 1074 #1000 1075 $stop; 1076 end 1077 1078 soc_top soc_top 1079 ( 1080 .clk (clk), 1081 .rst (rst) 1082 ); 1083 1084 1085 endmodule 2>编写汇编代码及手动汇编 当然还要编写其汇编代码,如下: 然后我们要手动汇编成机器码,指令集都是自己定义的,所以是没有现成的compiler,只能手动汇编了,还好手动汇编要比手动反汇编轻松多了(之前,我们手动反汇编过OpenRISC的启动代码)。 汇编完成后,我们将机器码放到指令存储器里,如下,共七条指令。 3>仿真结果 完成上面的工作之后,我们就可以用仿真工具进行仿真了,下面是我用modelsim仿真的结果。 从波形可以清晰的看出七条指令的执行过程。在运算完成后拉高write_e信号并将1+2的运算结果3写到了ram地址是55(0x37)的地方。 6,综合 完成了仿真之后,我们就可以综合一下,在FPGA上验证了,下面是我用quartusII 12.0综合的结果。 soc_top模块: core模块: data_path模块: 7,改进及优化 咱们设计现在这个小cpu core的目的是为了学习,在于了解cpu内部的运行机制,与软件的交互过程,没有考虑性能,功耗,面积等问题。 下面,我给出了采用五级流水的数据通路的模块划分和接口定义,如下所示,感兴趣的兄弟可以试着实现她。 8,future work 1,pipeline,将core流水化 2,增加乘法,除法指令。4-bit乘法器和除法器的设计: 3,fix bug 4,设计tiny_core的assembler,这样就不用手动汇编了。 9,小结 本小节,我们试着设计了一个简单的cpu,并编写了可综合的外部基本模块和不可综合的测试激励模块,然后用quartusII进行了综合,最后还给出了优化后的数据通路结构。 我想,通过本小节的内容,应该对cpu的工作机制和软件与硬件的关系有了更深入的感觉了。 需要说明的是,我的水平有限,再加上时间很短(两周),所以这里面肯定还有很多问题,和需要改进的地方,如果感兴趣,可以把代码down下来,先用modelsim仿真一下,然后再写个I/O controller,比如uart的控制器,挂到数据总线上,如果PC机能收到tiny_soc发出的“hello world”,那是多么令人激动的一件事情啊。
中央处理器的设计,实现,仿真与综合.doc
(859.17 KB, 下载次数: 10
)
|
|
相关推荐
|
|
又涨姿势了,厉害
|
|
|
|
|
|
开启全新AI时代 智能嵌入式系统快速发展——“第六届国产嵌入式操作系统技术与产业发展论坛”圆满结束
2360 浏览 0 评论
2024 RT-Thread全球巡回线下培训火热来袭!报名提问有奖!
22479 浏览 11 评论
2885 浏览 0 评论
IaaS+on+DPU(IoD)+下一代高性能算力底座技术白皮书
4296 浏览 0 评论
飞凌嵌入式-ELFBOARD 常用的USB接口及其不同版本介绍第1期
1338 浏览 0 评论
70264 浏览 21 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-10-15 02:13 , Processed in 1.173243 second(s), Total 68, Slave 50 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号