《自己动手写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) beginif (ce == `ChipDisable) beginpc <= 32'h00000000;end else beginpc <= pc + 4'h4;endendalways @ (posedge clk) beginif (rst == `RstEnable) begince <= `ChipDisable;end else begince <= `ChipEnable;endend input wire[`InstAddrBus] if_pc,input wire[`InstBus] if_inst,output reg[`InstAddrBus] id_pc,output reg[`InstBus] id_inst always @ (posedge clk) beginif (rst == `RstEnable) beginid_pc <= `ZeroWord;id_inst <= `ZeroWord;end else beginid_pc <= if_pc;id_inst <= if_inst;endend可以看到以上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
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總線互聯矩陣的從設備接口2gpio_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總線互聯矩陣的從設備接口3flash_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總線互聯矩陣的從設備接口1uart_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總線互聯矩陣的從設備接口0sdrc_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呢?
總結
以上是生活随笔為你收集整理的《自己动手写cpu》读书笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: INS/GNSS组合导航(五)惯性器件的
- 下一篇: JS正则表达式匹配域名