About porting the MIPSfpga project



    Transferring software from one computer architecture to another, in principle, with some reservations, is relatively simple. Well-known tools like autoconf / automake / libtool / gnulib come to the rescue here. Compiling a program from source on a Raspberry / ARM is as simple as on a PC with Ubuntu / x86-64.

    But how to make the FPGA project designed for one board work on another board? There, the FPGA itself may be different and completely different components can stand on the board. A simple recompilation of the project is indispensable.

    I’ll tell you about my experience in porting the MIPSfpga project for the Mars rover3 board with Intel MAX10 FPGA. Articles about the MIPSfpga project have repeatedly appeared on the hub They were so interesting that I wanted to try this project myself in my existing board. In my work, I relied on Khabrovsk articles


    And many others ...

    So, what needs to be done to port the FPGA project to another board?

    1) It is necessary to reassign the type of FPGA used in the project


    It’s lucky if the original project is focused on the exact same FPGA chip as on your motherboard. The MIPSfpga project has already been ported to boards from different manufacturers, including boards with Xilinx FPGAs and Intel / Altera FPGA boards. Nevertheless, let’s say so, I’m “out of luck”. Yes, there is a project for the DE10-lite board and it uses the MAX10 FPGA, as on my board. But this is a completely different MAX10 - on the DE10-lite it costs 10M50DAF484C7G, and on the Mars rover3 board there is the FPGA MAX1050SAE144C8. Both chips have 50 thousand logic gates, but the first has much more input / output signals. It is clear that, together with the reassignment of the type of FPGA chip, all signal assignments to specific pin of the chip “fly off”.



    The type of chip used in the Intel Quartus CAD environment is assigned through the Assignments => Device menu in the Device dialog box.

    There is also a Device And Pin Options button here - go there to the next dialog box and I highly recommend immediately indicating that the unused pins in the project should be As input tri-state.



    If the board is not very familiar, for example, we have not thoroughly studied its circuit, it is better to disconnect unused FPGA conclusions from the circuitry of the board.

    2) Make new assignments to inputs and outputs


    Since the type of microcircuit was reassigned, now you need to specify which inputs / outputs go to certain signals of the FPGA project. In each FPGA project, there is a module of the highest level. The signals of this module are connected to the terminals of the FPGA chip, with those where we specify in the project settings.

    Here, for example, in my project:

    module m3
    (
        input           MAX10_CLK_100,
        input   [ 1:0]  KEY,
        output  [ 7:0]  LEDR,
    	 input wire  FTDI_RX,
    	 output wire FTDI_TX,
    	 inout wire [7:0]FTD,
        `ifdef MFP_USE_SDRAM_MEMORY
            output  [11:0]  DRAM_ADDR,
            output  [ 1:0]  DRAM_BA,
            output          DRAM_CAS_N,
            //output          DRAM_CKE,
            output          DRAM_CLK,
            //output          DRAM_CS_N,
            inout   [15:0]  DRAM_DQ,
            output          DRAM_LDQM,
            output          DRAM_RAS_N,
            output          DRAM_UDQM,
            output          DRAM_WE_N,
        `endif
        `ifdef UNUSED
    	     input           ADC_CLK_10,
    		  input           MAX10_CLK1_50,
    		  input           MAX10_CLK2_50,
    		  input   [ 9:0]  SW,
    		  output  [ 7:0]  HEX0,
    		  output  [ 7:0]  HEX1,
    		  output  [ 7:0]  HEX2,
    		  output  [ 7:0]  HEX3,
    		  output  [ 7:0]  HEX4,
    		  output  [ 7:0]  HEX5,
            output  [ 3:0]  VGA_B,
            output  [ 3:0]  VGA_G,
            output          VGA_HS,
            output  [ 3:0]  VGA_R,
            output          VGA_VS,
            output          GSENSOR_CS_N,
            input   [ 2:1]  GSENSOR_INT,
            output          GSENSOR_SCLK,
            inout           GSENSOR_SDI,
            inout           GSENSOR_SDO,
            inout   [15:0]  ARDUINO_IO,
            inout           ARDUINO_RESET_N,
    		  inout   [35:0]  GPIO,
        `endif
    		  inout   [15:0]  GPIO,
    		  //HDMI output
    		  output wire [7:0]TMDS
    );

    The Mars rover3 board has neither SW switches, nor seven-segment indicators HEX0..HEX5, nor a VGA connector. I did not directly remove these signals from the project, but deleted them by conditional compilation using the `if UNUSED ...` endif construct. The Mars rover3 board has LEDs, buttons, FTDI signals and even HDMI signals - these are present in my project.



    In the Quartus environment, through the Assigments => Assignment Editor menu, you need to set the signal correspondence to the chip contact number. Of course, this must be done according to the circuit diagram of the circuit board. In addition to specifying the pin number of the microcircuit, you may need to make other appointments like i / O Standard or Pull-Up Resistor, if necessary.

    3) Attention to the clock frequency


    It is likely that the original project was made for a board in which the nominal value of the quartz generator is not the same as on our board. And it turned out in my case. The DE10-lite board has a 50MHz oscillator, and my board is 100MHz. What you need to do is to recreate the PLL module instance for this project. In the quartus menu, select Tools => IP Catalog, among Installed IPs => Libraries we find basic Functions => Clocks; PLLs and Reset => PLLs => ALTPLL and run the Wizard.



    You need to specify the correct source frequency and in PLL set the desired output frequency. There are many factors to consider. I decided to add to the project the output to the HDMI display, which was not in the original project, which means I needed additional frequencies required directly for the formation of TMDS video signals (74 MHz and 370 MHz).

    The created variation of the PLL component must then be inserted into the project.

    4) Trim or expand functionality?


    In the case of porting FPGA projects to other boards, you often come across the fact that it will not be possible to repeat the project one-on-one. The simplest reason is that, for example, the original project was made for a board that has 16 LEDs and / or seven-segment indicators, but on our board, for example, there is none at all, or there are LEDs, but there are less than sixteen, but only eight. Or on the board there is another amount of RAM or flash ... Then you have to come up with something, somehow circumvent the restrictions.

    I decided that since the MIPSfpga project training and displaying the information really somehow needs to be done, otherwise the “project visibility” is lost, so I will display the information on the monitor.

    For this purpose, I wrote a module of virtual LEDs and seven-segment indicators on Verilog.

    I will give the text of the module here in its entirety:

    Virtual LED and Indicator Module
    //marsohod3 board has only 8 yellow LEDs
    //but sample MIPS needs red/green LEDs and 7-segment indicators
    //so try display LEDs and 7-segment info on HDMI output of Marsohod3 board
    module display 
    		(
    		input wire reset,
    		input wire clk_video,	//74MHz
    		input wire clk_hdmi,		//370MHz
    		input wire [NUM_RED_LEDS-1:0]red_leds,
    		input wire [NUM_GREEN_LEDS-1:0]green_leds,
    		input wire [NUM_SEGMENTS*4-1:0]segments,
    		//HDMI output
    		output wire [7:0]TMDS
    	   );
    parameter NUM_RED_LEDS = 16;
    parameter NUM_GREEN_LEDS = 16;
    parameter NUM_SEGMENTS = 6;
    wire w_hsync;
    wire w_vsync;
    wire w_active;
    wire [11:0]w_pixel_count;
    wire [11:0]w_line_count;
    wire w_video_clk5; assign w_video_clk5 = clk_hdmi;
    wire w_video_clk; assign w_video_clk = clk_video;
    hvsync u_hvsync(
    	.reset( reset ),
    	.pixel_clock( w_video_clk ),
    	.hsync(w_hsync),
    	.vsync(w_vsync),
    	.active(w_active),
    	.pixel_count(w_pixel_count),
    	.line_count(w_line_count),
    	.dbg( )
    	);
    reg d_active;
    reg r_hsync;
    reg r_vsync;
    reg r_vsync_;
    reg [NUM_RED_LEDS-1:0]red_leds_fixed;
    reg [NUM_GREEN_LEDS-1:0]green_leds_fixed;
    reg [NUM_SEGMENTS*4-1:0]segments_fixed;
    always @(posedge w_video_clk)
    begin
    	r_hsync  <= w_hsync;
    	r_vsync  <= w_vsync;
    	r_vsync_ <= r_vsync;
    	d_active <= w_active;
    	if( r_vsync_==1'b0 && r_vsync==1'b1 )
    	begin
    		red_leds_fixed <= red_leds;
    		green_leds_fixed <= green_leds;
    		segments_fixed <= segments;
    	end
    end
    reg [9:0]red_word;
    reg [9:0]green_word;
    reg [9:0]blue_word;
    wire led_visible; assign led_visible = w_pixel_count<512 && w_pixel_count[4]==1'b1;
    wire [3:0]led_idx; assign led_idx = NUM_RED_LEDS-1-w_pixel_count[8:5];
    wire current_green_led = (green_leds_fixed >> led_idx)&1;
    wire current_red_led = (red_leds_fixed >> led_idx)&1;
    wire h_seg_line5; assign h_seg_line5 = w_pixel_count<(64*1-8) && w_pixel_count>(64*0+32);
    wire h_seg_line4; assign h_seg_line4 = w_pixel_count<(64*2-8) && w_pixel_count>(64*1+32);
    wire h_seg_line3; assign h_seg_line3 = w_pixel_count<(64*3-8) && w_pixel_count>(64*2+32);
    wire h_seg_line2; assign h_seg_line2 = w_pixel_count<(64*4-8) && w_pixel_count>(64*3+32);
    wire h_seg_line1; assign h_seg_line1 = w_pixel_count<(64*5-8) && w_pixel_count>(64*4+32);
    wire h_seg_line0; assign h_seg_line0 = w_pixel_count<(64*6-8) && w_pixel_count>(64*5+32);
    wire vl_seg_line5; assign vl_seg_line5 = w_pixel_count<(64*1-32) && w_pixel_count>(64*0+24);
    wire vl_seg_line4; assign vl_seg_line4 = w_pixel_count<(64*2-32) && w_pixel_count>(64*1+24);
    wire vl_seg_line3; assign vl_seg_line3 = w_pixel_count<(64*3-32) && w_pixel_count>(64*2+24);
    wire vl_seg_line2; assign vl_seg_line2 = w_pixel_count<(64*4-32) && w_pixel_count>(64*3+24);
    wire vl_seg_line1; assign vl_seg_line1 = w_pixel_count<(64*5-32) && w_pixel_count>(64*4+24);
    wire vl_seg_line0; assign vl_seg_line0 = w_pixel_count<(64*6-32) && w_pixel_count>(64*5+24);
    wire vr_seg_line5; assign vr_seg_line5 = w_pixel_count<(64*1) && w_pixel_count>(64*0+56);
    wire vr_seg_line4; assign vr_seg_line4 = w_pixel_count<(64*2) && w_pixel_count>(64*1+56);
    wire vr_seg_line3; assign vr_seg_line3 = w_pixel_count<(64*3) && w_pixel_count>(64*2+56);
    wire vr_seg_line2; assign vr_seg_line2 = w_pixel_count<(64*4) && w_pixel_count>(64*3+56);
    wire vr_seg_line1; assign vr_seg_line1 = w_pixel_count<(64*5) && w_pixel_count>(64*4+56);
    wire vr_seg_line0; assign vr_seg_line0 = w_pixel_count<(64*6) && w_pixel_count>(64*5+56);
    function  [7:0]seg7;
    input [3:0]a;
    begin
    	case(a)
    	0: seg7 = 63;
    	1: seg7 = 6;
    	2: seg7 = 91;
    	3: seg7 = 79;
    	4: seg7 = 102;
    	5: seg7 = 109;
    	6: seg7 = 125;
    	7: seg7 = 7;
    	8: seg7 = 127;
    	9: seg7 = 111;
    	10: seg7 = 119;
    	11: seg7 = 124;
    	12: seg7 = 57;
    	13: seg7 = 94;
    	14: seg7 = 121;
    	15: seg7 = 113;
    	endcase
    end
    endfunction
    reg [6:0]seg7_0;
    reg [6:0]seg7_1;
    reg [6:0]seg7_2;
    reg [6:0]seg7_3;
    reg [6:0]seg7_4;
    reg [6:0]seg7_5;
    always @(posedge clk_video)
    begin
    	seg7_0 <= seg7( segments_fixed[ 3: 0] );
    	seg7_1 <= seg7( segments_fixed[ 7: 4] );
    	seg7_2 <= seg7( segments_fixed[11: 8] );
    	seg7_3 <= seg7( segments_fixed[15:12] );
    	seg7_4 <= seg7( segments_fixed[19:16] );
    	seg7_5 <= seg7( segments_fixed[23:20] );
    end
    reg [2:0]color;
    always @*
    begin
    	if( w_line_count>64 && w_line_count<80 ) begin
    		//green led line
    		if(led_visible)
    			color = current_green_led ? 1 : 2;
    		else color=0;
    	end
    	else
    	if( w_line_count>96 && w_line_count<112 ) begin
    		//red led line
    		if(led_visible)
    			color = current_red_led ? 3 : 4;
    		else color=0;
    	end
    	else
    	if( w_line_count>118+32 && w_line_count<126+32 ) begin
    		// 7seg top line
    		if(h_seg_line0)
    			color = seg7_0[0] ? 5 : 6;
    		else
    		if(h_seg_line1)
    			color = seg7_1[0] ? 5 : 6;
    		else 
    		if(h_seg_line2)
    			color = seg7_2[0] ? 5 : 6;
    		else 
    		if(h_seg_line3)
    			color = seg7_3[0] ? 5 : 6;
    		else 
    		if(h_seg_line4)
    			color = seg7_4[0] ? 5 : 6;
    		else 
    		if(h_seg_line5)
    			color = seg7_5[0] ? 5 : 6;
    		else 
    			color=0;
    	end
    	else
    	if( w_line_count>126+32 && w_line_count<148+32 ) begin
    		if(vr_seg_line0)
    			color = seg7_0[1] ? 5 : 6;
    		else
    		if(vr_seg_line1)
    			color = seg7_1[1] ? 5 : 6;
    		else 
    		if(vr_seg_line2)
    			color = seg7_2[1] ? 5 : 6;
    		else 
    		if(vr_seg_line3)
    			color = seg7_3[1] ? 5 : 6;
    		else 
    		if(vr_seg_line4)
    			color = seg7_4[1] ? 5 : 6;
    		else 
    		if(vr_seg_line5)
    			color = seg7_5[1] ? 5 : 6;
    		else 
    		if(vl_seg_line0)
    			color = seg7_0[5] ? 5 : 6;
    		else
    		if(vl_seg_line1)
    			color = seg7_1[5] ? 5 : 6;
    		else 
    		if(vl_seg_line2)
    			color = seg7_2[5] ? 5 : 6;
    		else 
    		if(vl_seg_line3)
    			color = seg7_3[5] ? 5 : 6;
    		else 
    		if(vl_seg_line4)
    			color = seg7_4[5] ? 5 : 6;
    		else 
    		if(vl_seg_line5)
    			color = seg7_5[5] ? 5 : 6;
    		else
    			color=0;
    	end
    	else
    	if( w_line_count>148+32 && w_line_count<156+32 ) begin
    		// 7seg middle line
    		if(h_seg_line0)
    			color = seg7_0[6] ? 5 : 6;
    		else
    		if(h_seg_line1)
    			color = seg7_1[6] ? 5 : 6;
    		else 
    		if(h_seg_line2)
    			color = seg7_2[6] ? 5 : 6;
    		else 
    		if(h_seg_line3)
    			color = seg7_3[6] ? 5 : 6;
    		else 
    		if(h_seg_line4)
    			color = seg7_4[6] ? 5 : 6;
    		else 
    		if(h_seg_line5)
    			color = seg7_5[6] ? 5 : 6;
    		else 
    			color=0;
    	end
    	else
    	if( w_line_count>156+32 && w_line_count<180+32 ) begin
    		if(vr_seg_line0)
    			color = seg7_0[2] ? 5 : 6;
    		else
    		if(vr_seg_line1)
    			color = seg7_1[2] ? 5 : 6;
    		else 
    		if(vr_seg_line2)
    			color = seg7_2[2] ? 5 : 6;
    		else 
    		if(vr_seg_line3)
    			color = seg7_3[2] ? 5 : 6;
    		else 
    		if(vr_seg_line4)
    			color = seg7_4[2] ? 5 : 6;
    		else 
    		if(vr_seg_line5)
    			color = seg7_5[2] ? 5 : 6;
    		else 
    		if(vl_seg_line0)
    			color = seg7_0[4] ? 5 : 6;
    		else
    		if(vl_seg_line1)
    			color = seg7_1[4] ? 5 : 6;
    		else 
    		if(vl_seg_line2)
    			color = seg7_2[4] ? 5 : 6;
    		else 
    		if(vl_seg_line3)
    			color = seg7_3[4] ? 5 : 6;
    		else 
    		if(vl_seg_line4)
    			color = seg7_4[4] ? 5 : 6;
    		else 
    		if(vl_seg_line5)
    			color = seg7_5[4] ? 5 : 6;
    		else
    			color=0;
    	end
    	else
    	if( w_line_count>180+32 && w_line_count<188+32 ) begin
    		// 7seg bottom line
    		if(h_seg_line0)
    			color = seg7_0[3] ? 5 : 6;
    		else
    		if(h_seg_line1)
    			color = seg7_1[3] ? 5 : 6;
    		else 
    		if(h_seg_line2)
    			color = seg7_2[3] ? 5 : 6;
    		else 
    		if(h_seg_line3)
    			color = seg7_3[3] ? 5 : 6;
    		else 
    		if(h_seg_line4)
    			color = seg7_4[3] ? 5 : 6;
    		else 
    		if(h_seg_line5)
    			color = seg7_5[3] ? 5 : 6;
    		else 
    			color=0;
    	end
    	else
    		color=0;
    end
    reg [2:0]color_fixed;
    always @(posedge clk_video)
    	color_fixed<=color;
    always @*
    begin
    	case(color_fixed)
    	0: begin //gray
    			red_word   = 10'b0000011111;
    			green_word = 10'b0000011111;
    			blue_word  = 10'b0000111111;
    		end
    	1: begin //bright green
    			red_word   = 10'b0000011111;
    			green_word = 10'b1111111111;
    			blue_word  = 10'b0000011111;
    		end
    	2: begin //dark green
    			red_word   = 10'b0000011111;
    			green_word = 10'b0000111111;
    			blue_word  = 10'b0000011111;
    		end
    	3: begin //bright red
    			red_word   = 10'b1111111111;
    			green_word = 10'b0000011111;
    			blue_word  = 10'b0000011111;
    		end
    	4: begin //dark red
    			red_word   = 10'b0000111111;
    			green_word = 10'b0000011111;
    			blue_word  = 10'b0000011111;
    		end
    	5: begin //bright yellow
    			red_word   = 10'b1111111111;
    			green_word = 10'b1111111111;
    			blue_word  = 10'b0000011111;
    		end
    	6: begin //dark yellow
    			red_word   = 10'b0000111111;
    			green_word = 10'b0000111111;
    			blue_word  = 10'b0000011111;
    		end
    	7: begin //gray
    			red_word   = 10'b0000011111;
    			green_word = 10'b0000011111;
    			blue_word  = 10'b0000111111;
    		end		
    	endcase
    end
    wire w_tmds_bh;
    wire w_tmds_bl;
    wire w_tmds_gh;
    wire w_tmds_gl;
    wire w_tmds_rh;
    wire w_tmds_rl;
    hdmi u_hdmi(
    	.pixclk( w_video_clk ),
    	.clk_TMDS2( w_video_clk5 ),
    	.hsync( r_hsync ),
    	.vsync( r_vsync ),
    	.active( d_active ),
    	.red( red_word ),
    	.green( green_word ),
    	.blue( blue_word ),
    	.TMDS_bh( w_tmds_bh ),
    	.TMDS_bl( w_tmds_bl ),
    	.TMDS_gh( w_tmds_gh ),
    	.TMDS_gl( w_tmds_gl ),
    	.TMDS_rh( w_tmds_rh ),
    	.TMDS_rl( w_tmds_rl )
    );
    altddio_out1 u_ddio1( .datain_h( w_video_clk), .datain_l( w_video_clk), .outclock(w_video_clk5), .dataout( TMDS[1] ) );
    altddio_out1 u_ddio0( .datain_h(~w_video_clk), .datain_l(~w_video_clk), .outclock(w_video_clk5), .dataout( TMDS[0] ) );
    altddio_out1 u_ddio3( .datain_h( w_tmds_bh),   .datain_l( w_tmds_bl),   .outclock(w_video_clk5), .dataout( TMDS[3] ) );
    altddio_out1 u_ddio2( .datain_h(~w_tmds_bh),   .datain_l(~w_tmds_bl),   .outclock(w_video_clk5), .dataout( TMDS[2] ) );
    altddio_out1 u_ddio5( .datain_h( w_tmds_gh),   .datain_l( w_tmds_gl),   .outclock(w_video_clk5), .dataout( TMDS[5] ) );
    altddio_out1 u_ddio4( .datain_h(~w_tmds_gh),   .datain_l(~w_tmds_gl),   .outclock(w_video_clk5), .dataout( TMDS[4] ) );
    altddio_out1 u_ddio7( .datain_h( w_tmds_rh),   .datain_l( w_tmds_rl),   .outclock(w_video_clk5), .dataout( TMDS[7] ) );
    altddio_out1 u_ddio6( .datain_h(~w_tmds_rh),   .datain_l(~w_tmds_rl),   .outclock(w_video_clk5), .dataout( TMDS[6] ) );
    endmodule
    

    Here is the entire project source code for the Mars rover3 board , which demonstrates the display of a binary counter on virtual LEDs.

    And here is his video demonstration:


    It seems to me that it turned out pretty nice.

    Thus, with the help of virtual LEDs and seven-segment indicators, I was able to save a significant part of the MIPSfpga project when porting to a board that physically does not have 32 LEDs and does not have 7 segment indicators.

    Next, I will show how the MIPSfpga project loads on the board:


    This video demonstration shows how the FPGA board is loaded from a laptop from the Quartus Prime CAD environment and immediately after loading the FPGA, the connected HDMI monitor receives synchronization. Then, the counter test program is compiled on the laptop and loaded into the board and launched on the MIPS processor in FPGA.

    Here are more sample programs for MIPS:


    1. The memory test program 04_memtest is launched, then
    2. We are exploring the 05_uart communication program for the MIPSfpga SoC and for the serial port from a PC,
    3. program 06_timer_irq works using hardware interrupts
    4. 09_adc_0 is a test program built into the FPGA MAX10 ADC

    Well, I would also like to show how you can conduct in-circuit debugging of MIPS programs using the external MBFTDI programmer and OpenOCD and GDB programs:


    You can read more about the MIPSfpga project in the Mars rover3 board here .

    In conclusion, I would like to thank SparF , YuriPanchul , Frantony for their wonderful series of articles on MIPSfpga. Without these articles, this would not have been my work.

    Also popular now: