《自己动手写cpu》读书笔记

本文来自《自己动手写cpu》一书的总结。原来自己看过原作者的《步步惊芯--软核处理器分析》以及其他关于or1200的书。本次粗略浏览了该书,就某些感兴趣的部分详细分析,并总结成此文。

关于5级流水的架构,可以自己去参考《计算机接口》一书。本文重点不在此。

1、如何从rom里面取地址

简化版的最基本的sopc的框图如下:

module openmips(
	input	wire			clk,
	input wire			rst,
	input wire[`RegBus]           rom_data_i,
	output wire[`RegBus]           rom_addr_o,
	output wire                    rom_ce_o
);
always @ (posedge clk) begin
		if (ce == `ChipDisable) begin
			pc <= 32'h00000000;
		end else begin
	 		pc <= pc + 4'h4;
		end
	end

	always @ (posedge clk) begin
		if (rst == `RstEnable) begin
			ce <= `ChipDisable;
		end else begin
			ce <= `ChipEnable;
		end
	end
	input wire[`InstAddrBus]      if_pc,
	input wire[`InstBus]          if_inst,
	output reg[`InstAddrBus]      id_pc,
	output reg[`InstBus]          id_inst  

	always @ (posedge clk) begin
		if (rst == `RstEnable) begin
			id_pc <= `ZeroWord;
			id_inst <= `ZeroWord;
	  end else begin
		  id_pc <= if_pc;
		  id_inst <= if_inst;
		end
	end

可以看到以上3个代码段,第一段是openmips中的,表示怎么连接含有指令的rom,第二段是pc_reg.v中的,pc是自加的,然后一直是可以对rom取址的,第3段是if_id.v中的,可以看到取址后传递给了译码模块。这就是最基本的程序跑起来的开始。

后面作者自己收东西写指令码实现了与寄存器s0的操作,按照$readmemh的要求写成
inst_rom.data。inst_rom.v中读入了该文件。可以参考$readmemh的语法。

2、协处理器的概念:

比如说这里的定时器中断时间就应该很有用。

3、异常相关指令的实现

这里的定时器中断设置,是自己去写的汇编代码,然后验证指令正确。实际上我们一般是用c语言去写程序,编译器生成汇编代码和机器码。所以这些异常指令实际上应该是编译器去生产的,异常程序入口地址也不是我们操心的。但是这里我们只能自己写了。

4、wishbone总线:

说明:根据上面那个图,可以看到哈佛结构的指令rom和数据ram,都是例化了wishbone接口,均采用点对点的方式,连接到外部的有同样wishbone接口的rom和ram中。由于只是采用了这种接口协议,针对具体的情况还得在中间再加上一层接口模块,即上图所示的状态机,一部分是控制状态转化的时序电路,另一部分是给处理器接口信号赋值的组合电路。

module openmips(
	input	wire		      clk,
	input wire		      rst,
        input wire[5:0]               int_i,

  //指令wishbone总线
	input wire[`RegBus]           iwishbone_data_i,
	input wire                    iwishbone_ack_i,
	output wire[`RegBus]           iwishbone_addr_o,
	output wire[`RegBus]           iwishbone_data_o,
	output wire                    iwishbone_we_o,
	output wire[3:0]               iwishbone_sel_o,
	output wire                    iwishbone_stb_o,
	output wire                    iwishbone_cyc_o, 

  //数据wishbone总线
	input wire[`RegBus]           dwishbone_data_i,
	input wire                    dwishbone_ack_i,
	output wire[`RegBus]           dwishbone_addr_o,
	output wire[`RegBus]           dwishbone_data_o,
	output wire                    dwishbone_we_o,
	output wire[3:0]               dwishbone_sel_o,
	output wire                    dwishbone_stb_o,
	output wire                    dwishbone_cyc_o,

	output wire                    timer_int_o
);
wishbone_bus_if dwishbone_bus_if(
		.clk(clk),
		.rst(rst),

		//来自控制模块ctrl
		.stall_i(stall),
		.flush_i(flush),

		//CPU侧读写操作信息
		.cpu_ce_i(ram_ce_o),
		.cpu_data_i(ram_data_o),
		.cpu_addr_i(ram_addr_o),
		.cpu_we_i(ram_we_o),
		.cpu_sel_i(ram_sel_o),
		.cpu_data_o(ram_data_i),

		//Wishbone总线侧接口
		.wishbone_data_i(dwishbone_data_i),
		.wishbone_ack_i(dwishbone_ack_i),
		.wishbone_addr_o(dwishbone_addr_o),
		.wishbone_data_o(dwishbone_data_o),
		.wishbone_we_o(dwishbone_we_o),
		.wishbone_sel_o(dwishbone_sel_o),
		.wishbone_stb_o(dwishbone_stb_o),
		.wishbone_cyc_o(dwishbone_cyc_o),

		.stallreq(stallreq_from_mem)
);

	wishbone_bus_if iwishbone_bus_if(
		.clk(clk),
		.rst(rst),

		//来自控制模块ctrl
		.stall_i(stall),
		.flush_i(flush),

		//CPU侧读写操作信息
		.cpu_ce_i(rom_ce),
		.cpu_data_i(32'h00000000),
		.cpu_addr_i(pc),
		.cpu_we_i(1'b0),
		.cpu_sel_i(4'b1111),
		.cpu_data_o(inst_i),

		//Wishbone总线侧接口
		.wishbone_data_i(iwishbone_data_i),
		.wishbone_ack_i(iwishbone_ack_i),
		.wishbone_addr_o(iwishbone_addr_o),
		.wishbone_data_o(iwishbone_data_o),
		.wishbone_we_o(iwishbone_we_o),
		.wishbone_sel_o(iwishbone_sel_o),
		.wishbone_stb_o(iwishbone_stb_o),
		.wishbone_cyc_o(iwishbone_cyc_o),

		.stallreq(stallreq_from_if)
);

该章节源代码并没有给出有wishbone接口的rom和ram的源代码,但是从第一段的代码中可以看到实际上是要接有这么一种接口的rom和ram的。第二段代码可以看到分别实现了指令和数据的wb接口控制模块,直接接到外面的ram和rom。

5、小型的SOPC

module openmips_min_sopc(
	input	wire			clk,
	input wire			rst,
	
        //新增uart接口
	input wire                   uart_in,
	output wire                   uart_out,

	//16位GPIO输入接口
	input wire[15:0]             gpio_i,
</pre><pre code_snippet_id="1671506" snippet_file_name="blog_20160504_9_3260604" name="code" class="plain">        //32位GPIO输出接口
	output wire[31:0]            gpio_o,
	//flash接口
	input wire[7:0]             flash_data_i,
	output wire[21:0]           flash_addr_o,
	output wire                 flash_we_o,
	output wire                 flash_rst_o,
	output wire                 flash_oe_o,
	output wire                 flash_ce_o,  
<span style="font-family: Arial, Helvetica, sans-serif;">                 //sdrsm接口</span>
	output wire sdr_clk_o,
  output wire sdr_cs_n_o,
  output wire sdr_cke_o,
 output wire sdr_ras_n_o,
  output wire sdr_cas_n_o,
  output wire sdr_we_n_o,
  output wire[1:0] sdr_dqm_o,
  output wire[1:0] sdr_ba_o,
  output wire[12:0] sdr_addr_o,
   inout wire[15:0] sdr_dq_io
);
 openmips openmips0(
		.clk(clk),
		.rst(rst),
		// 指令wb总线接口连到wb总线互联矩阵的主设备接口1
		.iwishbone_data_i(m1_data_o),
		.iwishbone_ack_i(m1_ack_o),
		.iwishbone_addr_o(m1_addr_i),
		.iwishbone_data_o(m1_data_i),
		.iwishbone_we_o(m1_we_i),
		.iwishbone_sel_o(m1_sel_i),
		.iwishbone_stb_o(m1_stb_i),
		.iwishbone_cyc_o(m1_cyc_i), 

		.int_i(int),
		// 数据wb总线接口连到wb总线互联矩阵的主设备接口0
		.dwishbone_data_i(m0_data_o),
		.dwishbone_ack_i(m0_ack_o),
		.dwishbone_addr_o(m0_addr_i),
		.dwishbone_data_o(m0_data_i),
		.dwishbone_we_o(m0_we_i),
		.dwishbone_sel_o(m0_sel_i),
		.dwishbone_stb_o(m0_stb_i),
		.dwishbone_cyc_o(m0_cyc_i),

		.timer_int_o(timer_int)	

);
	// GPIO连到wb总线互联矩阵的从设备接口2
	gpio_top gpio_top0(
    .wb_clk_i(clk),
		.wb_rst_i(rst),
		.wb_cyc_i(s2_cyc_o),
		.wb_adr_i(s2_addr_o[7:0]),
		.wb_dat_i(s2_data_o),
		.wb_sel_i(s2_sel_o),
		.wb_we_i(s2_we_o),
		.wb_stb_i(s2_stb_o),
	  .wb_dat_o(s2_data_i),
		.wb_ack_o(s2_ack_i),
		.wb_err_o(),
		.wb_inta_o(gpio_int),
		.ext_pad_i(gpio_i_temp),
		.ext_pad_o(gpio_o),
		.ext_padoe_o()
  );
	// fiash控制器连到wb总线互联矩阵的从设备接口3
	flash_top flash_top0(
    .wb_clk_i(clk),
    .wb_rst_i(rst),
    .wb_adr_i(s3_addr_o),
    .wb_dat_o(s3_data_i),
    .wb_dat_i(s3_data_o),
    .wb_sel_i(s3_sel_o),
    .wb_we_i(s3_we_o),
    .wb_stb_i(s3_stb_o),
    .wb_cyc_i(s3_cyc_o),
    .wb_ack_o(s3_ack_i),

	//与小型sopc外部接口相连,对外是flash芯片
    .flash_adr_o(flash_addr_o),
    .flash_dat_i(flash_data_i),
    .flash_rst(flash_rst_o),
    .flash_oe(flash_oe_o),
    .flash_ce(flash_ce_o),
    .flash_we(flash_we_o)
  );
	// uart控制器连到wb总线互联矩阵的从设备接口1
	uart_top	uart_top0(
	   .wb_clk_i(clk),
	   .wb_rst_i(rst),
	   .wb_adr_i(s1_addr_o[4:0]),
	   .wb_dat_i(s1_data_o),
	   .wb_dat_o(s1_data_i),
	   .wb_we_i(s1_we_o),
	   .wb_stb_i(s1_stb_o),
	   .wb_cyc_i(s1_cyc_o),
	   .wb_ack_o(s1_ack_i),
	   .wb_sel_i(s1_sel_o),

	   .int_o(uart_int),
	   //连接uart接口
	   .stx_pad_o(uart_out),
	   .srx_pad_i(uart_in),
	   .cts_pad_i(1'b0),
	   .dsr_pad_i(1'b0),
	   .ri_pad_i(1'b0),
	   .dcd_pad_i(1'b0),
	   .rts_pad_o(),
	   .dtr_pad_o()
	);
	// sdram控制器连到wb总线互联矩阵的从设备接口0
  sdrc_top sdrc_top0(
     .cfg_sdr_width(2'b01),
     .cfg_colbits(2'b00),

     .wb_rst_i(rst),
     .wb_clk_i(clk),

     .wb_stb_i(s0_stb_o),
     .wb_ack_o(s0_ack_i),
     .wb_addr_i({s0_addr_o[25:2],2'b00}),
     .wb_we_i(s0_we_o),
     .wb_dat_i(s0_data_o),
     .wb_sel_i(s0_sel_o),
     .wb_dat_o(s0_data_i),
     .wb_cyc_i(s0_cyc_o),
     .wb_cti_i(3'b000),

		//连接sdram
     .sdram_clk(clk),
     .sdram_resetn(~rst),
     .sdr_cs_n(sdr_cs_n_o),
     .sdr_cke(sdr_cke_o),
     .sdr_ras_n(sdr_ras_n_o),
     .sdr_cas_n(sdr_cas_n_o),
     .sdr_we_n(sdr_we_n_o),
     .sdr_dqm(sdr_dqm_o),
     .sdr_ba(sdr_ba_o),
     .sdr_addr(sdr_addr_o),
     .sdr_dq(sdr_dq_io),

		//Parameters
     .sdr_init_done(sdram_init_done),
     .cfg_req_depth(2'b11),
     .cfg_sdr_en(1'b1),
     .cfg_sdr_mode_reg(13'b0000000110001),
     .cfg_sdr_tras_d(4'b1000),
     .cfg_sdr_trp_d(4'b0010),
     .cfg_sdr_trcd_d(4'b0010),
     .cfg_sdr_cas(3'b100),
     .cfg_sdr_trcar_d(4'b1010),
     .cfg_sdr_twr_d(4'b0010),
     .cfg_sdr_rfsh(12'b011010011000),
	   .cfg_sdr_rfmax(3'b100)
  );

	wb_conmax_top wb_conmax_top0(
     	.clk_i(clk),
     	.rst_i(rst),

	    // Master 0 Interface,数据接口
	    .m0_data_i(m0_data_i),
	    .m0_data_o(m0_data_o),
	    .m0_addr_i(m0_addr_i),
	    .m0_sel_i(m0_sel_i),
	    .m0_we_i(m0_we_i),
	    .m0_cyc_i(m0_cyc_i),
	    .m0_stb_i(m0_stb_i),
	    .m0_ack_o(m0_ack_o), 

	    // Master 1 Interface,指令接口
	    .m1_data_i(m1_data_i),
	    .m1_data_o(m1_data_o),
	    .m1_addr_i(m1_addr_i),
	    .m1_sel_i(m1_sel_i),
	    .m1_we_i(m1_we_i),
	    .m1_cyc_i(m1_cyc_i),
	    .m1_stb_i(m1_stb_i),
	    .m1_ack_o(m1_ack_o), 

	    // Slave 0 Interface,sdram控制器
	    .s0_data_i(s0_data_i),
	    .s0_data_o(s0_data_o),
	    .s0_addr_o(s0_addr_o),
	    .s0_sel_o(s0_sel_o),
	    .s0_we_o(s0_we_o),
	    .s0_cyc_o(s0_cyc_o),
	    .s0_stb_o(s0_stb_o),
	    .s0_ack_i(s0_ack_i),
	    .s0_err_i(1'b0),
	    .s0_rty_i(1'b0),

	    // Slave 1 Interface,uart控制器
	    .s1_data_i(s1_data_i),
	    .s1_data_o(s1_data_o),
	    .s1_addr_o(s1_addr_o),
	    .s1_sel_o(s1_sel_o),
	    .s1_we_o(s1_we_o),
	    .s1_cyc_o(s1_cyc_o),
	    .s1_stb_o(s1_stb_o),
	    .s1_ack_i(s1_ack_i),
	    .s1_err_i(1'b0),
	    .s1_rty_i(1'b0),

	    // Slave 2 Interface,gpio接口
	    .s2_data_i(s2_data_i),
	    .s2_data_o(s2_data_o),
	    .s2_addr_o(s2_addr_o),
	    .s2_sel_o(s2_sel_o),
	    .s2_we_o(s2_we_o),
	    .s2_cyc_o(s2_cyc_o),
	    .s2_stb_o(s2_stb_o),
	    .s2_ack_i(s2_ack_i),
	    .s2_err_i(1'b0),
	    .s2_rty_i(1'b0),

	    // Slave 3 Interface,flash控制器
	    .s3_data_i(s3_data_i),
	    .s3_data_o(s3_data_o),
	    .s3_addr_o(s3_addr_o),
	    .s3_sel_o(s3_sel_o),
	    .s3_we_o(s3_we_o),
	    .s3_cyc_o(s3_cyc_o),
	    .s3_stb_o(s3_stb_o),
	    .s3_ack_i(s3_ack_i),
	    .s3_err_i(1'b0),
	    .s3_rty_i(1'b0),
	);

endmodule

从上面可以看到是怎么集成进去的。全书写了几个外设模块的原理,但是一个关键的wb_conmax的实现原理,其实现了总线的上述诸多功能,包括仲裁等。这里只是直接拿来用了,理清这些关系很重要。

为什么flash的地址是0x30000000开始,因为我们是把他接在从设备3了。wb_conmax有规定,根据从设备来设定地址区段。

这些外置的接口ip都是通过mcu操纵其ip内部的寄存器来实现的。比如gpio:

所以要知道外部io口上的数据,只需内核去读这个地址的RGPIO_IN的这个寄存器就可以了。

所以我们实际上理解的编译器,实际上只是将我们的c语言代码编译成最核心的流水线内核的操作指令,涉及到外设的部分仅仅是生成一些操作寄存器的指令,即编译器只是理解外设与寄存器一致,只需生成访存和写回的指令即可。

6、测试与验证

在上面实现后怎么验证,作者用de2的开发板:

由于与mips指令兼容,所以可以使用mips的编译器,生成了inst_rom.o的可重定位elf文件,通过ld文件生成可执行文件,但是有elf文件头,与我们期望的格式还是有很大差别,可以利用mips-sde-elf-objcopy得到.bin的纯二进制格式,这正是我们需要的。(如果是用modelsim仿真,还需将其生成modelsim中存储器初始化文件的格式生成.data文件)

de2中将利用控制面板程序擦写进flash。

注意在编译前要修改ram.ld文件,将其中的起始地址从0x00000000修改为0x30000000,因为前2个测试陈旭是在flash中运行的,而flash的起始地址是0x30000000.

例子3模拟了os的启动过程:

后面是应用程序:

怎么写入flash呢?

时间: 2024-07-30 20:26:16

《自己动手写cpu》读书笔记的相关文章

码农的产品思维培养第一节(人人都是产品经理读书笔记)

在前段时间,密集的推出Android学习记录之后,我觉得接下来的Android开发进入了一个精进演变的过程,革命性的东西略缺.每日更新特别新的东西也违背认知规律.所以以后关于Android方面的知识,碰到什么,然后记录什么. 而今天,在前一篇日志里面,我描述了我为什么要去理解"产品经理",从这一节开始,我要实施我的计划.所以,和Android记录一样,我要记录这个过程.对自己是一个回归总结吸收的过程,同时也希望能够帮助到更多的朋友,如果你也心存学习进取之心,如果你也如我一般疑惑未解心不

人人都是产品经理读书笔记(四)

补充:

《启示录:打造用户喜爱的产品》—— 读书笔记

这是一本非常不错的书,即使你可能只是一名开发工程师,也会有意想不到的收获! 如果你是一名产品经理,那就更不能错过了!不要留下遗憾! 这真的是一本很好的书,读每一遍都会有不同的收获,绝对让你震撼!我是会再读一遍或者N多遍的, 而能把这些内容转应用到实际中的人才是真正的高手,细细体会,在工作中好像已经有人在用了!惊讶!得抓紧时间了! 通过这本书,你将会知道一个合格的产品经理应该做什么,怎么做 本书主要讲解三个方面:人员.流程.产品 人员:产品从开始到完成过程中所有的参与者 流程:产品在开发过程中的所

产品经理学习笔记(二)------产品经理的工作职责(下)

二.产品经理的工作职责(下) 4.产品宣讲 ---宣讲对象:客服.市场.销售.运营.其他(开发进度到50%) ---宣讲目的:内部培训.获得认可 ---宣讲方式:内部推荐会(预测.演示.试用).注意控制(氛围.引导) ---宣讲目标:获得认可.帮助其他团队更好理解产品.协助其他团队更好开展工作 5.市场推广 ---对产品资料进行内容把关:网站.移动应用.印刷品等 ---主要针对:市场.公关.运营.销售 6.产品推出后的管理与迭代 ---运营数据的整理分析 ---深入一线体验产品 ---关注用户需

产品经理--读书静心的日子

入行教育,做教育产品工作,需要不断的进步. 一.了解产品开发.项目管理经验. 二.教育基础理论及相关知识. 小学阶段 (2016.2017不断的翻阅,有新的体会) 中学阶段(2018主攻方向)

谷歌和亚马逊如何做产品(读书笔记)

《产品经理》读书笔记

自从鼠标手犯病后,就刻意减少使用电脑的时间并且加强运动,目前已经完全康复,但是还是需要注意.因此更新博客的频率大大降低,但是也有时间多看看书,学习学习了! 最近看了<yes,产品经理>上下册,作者 汤圆 老马,文笔诙谐,把管理知识融入工作日常内容,浅显易懂,对于非管理专业的门外汉,还是不错的读物! 下面是摘抄的部分主要内容,个人认为比较有用的就记录下来. ------------------------------------------------ 制定产品价格策略的6步: 确定企业目标 冲

产品经理的那些事第一章读书笔记

1.一个产品经理的信仰:好产品能改变世界. 2.为什么要做产品经理:因为热爱,改变世界的方法有很多,技术可以改变世界,好的产品也可以,当然还有其他,但我热爱产品,一件事只有热爱了,才能持续不断的去做好,所以我选择了产品经理这条路. 3.产品是什么:产品是用来解决某个问题的东西. 4.产品经理为何而设:想要更了解产品与它面临的竞争情况,最终目的是要满足顾客的需求. 5.产品经理概念的进化: 分析: 1)行业形态不同:成熟行业vs.新兴行业 ①传统行业 概况:经过几十年乃至上百年的摸爬滚打,市场已经

【读书笔记】产品经理要做的事

文章链接:http://www.chanpin100.com/archives/44223 作为一个产品经理,不能只画图:产品经理更像是一个纽带,连接着各个环节,保持项目的正常运行. 在开始要做一个产品的时候,不能上来就画图,也不能告诉你需求就开始画图.应该先对需求进行筛选和挖掘:把伪需求去掉,挖掘出潜在需求. 1.分析产品的步骤:目标人群.使用场景.业务核心. 2.在团队中担任掌舵人,有目的的引导团队:激发团队灵感可以使用商业画布:客户分布.价值主张.渠道通路.客户关系.收入来源.核心资源.关

【读书笔记】神一样的产品经理(一)

 第一篇 产品经理 1.产品经理诞生的背景和价值 *很多入门级书里都会提到这一部分,本书讲了保洁诞生的第一个产品经理的故事. 2.很牛的产品经理(例子是乔布斯.郭靖) 1)几个重要特性:*影响力 *核心需求把控力 *创新力 *痴情力 2)产品经理的职责: *明确产品的目标用户群及其特征*获取.评估和管理用户需求*完成产品需求文档.产品原型和流程图*精通用户体验.交互设计和信息架构技能*项目管理.需求变更管理和需求验收*产品运营数据的分析和总结*提供运营.市场和销售等支撑 3)产品经理常犯的错误