PicoBlaze でキャラクタ液晶表示

今回は、Spartan-3A の PicoBlaze でキャラクタ液晶の表示を行ないました。
と言っても今回は、fpga ポートの入出力動作がうまくいかなかったので
BUSYチェックはしないで、時間待ちして表示させてます。

何のヒネリも無いので、そのままソース掲載でし。マイコンのアセンブラとVerilogです。

PicoBlaze のソース

CONSTANT        LCD_DATA,       01      ;
CONSTANT        LCD_CTRL_E,     02      ;
CONSTANT        LCD_CTRL_RS,    03      ; 0=Command, 1=Data
CONSTANT        LCD_CTRL_RW,    04      ; 0=write, 1=read
CONSTANT        LED,            05      ; LED port

                ADDRESS 000
INIT00:
                ENABLE  INTERRUPT
                CALL    LCD_INIT

MAIN00:         LOAD    s4, 50          ; P
                CALL    LCD_PUTCH
                LOAD    s4, 69          ; i
                CALL    LCD_PUTCH
                LOAD    s4, 63          ; c
                CALL    LCD_PUTCH
                LOAD    s4, 6F          ; o
                CALL    LCD_PUTCH
                LOAD    s4, 42          ; B
                CALL    LCD_PUTCH
                LOAD    s4, 6C          ; l
                CALL    LCD_PUTCH
                LOAD    s4, 61          ; a
                CALL    LCD_PUTCH
                LOAD    s4, 7A          ; z
                CALL    LCD_PUTCH
                LOAD    s4, 65          ; e
                CALL    LCD_PUTCH
                LOAD    s4, 21          ; !
                CALL    LCD_PUTCH
                LOAD    s4, 00
MAIN90:
                CALL    DELAY20ms
                CALL    DELAY20ms
                CALL    DELAY20ms
                CALL    DELAY20ms
                CALL    DELAY20ms
                CALL    DELAY20ms
                CALL    DELAY20ms
                CALL    DELAY20ms
                CALL    DELAY20ms
                CALL    DELAY20ms
                OUTPUT  s4, LED        ; LED チカチカ
                ADD     s4, 01
                JUMP    MAIN90


LCD_INIT:       CALL    DELAY20ms
                CALL    DELAY20ms
                LOAD    s4, 30
                CALL    LCD_PUTCMD
                CALL    DELAY20ms
                LOAD    s4, 30
                CALL    LCD_PUTCMD
                CALL    DELAY1ms
                LOAD    s4, 30
                CALL    LCD_PUTCMD
                CALL    DELAY1ms
                LOAD    s4, 38
                CALL    LCD_PUTCMD
                CALL    DELAY100us
                LOAD    s4, 0F
                CALL    LCD_PUTCMD
                CALL    DELAY100us
                LOAD    s4, 06
                CALL    LCD_PUTCMD
                CALL    DELAY100us
                LOAD    s4, 01
                CALL    LCD_PUTCMD
                CALL    DELAY100us
                RETURN

LCD_PUTCH:      CALL    LCD_RS_1
                CALL    LCD_RW_0
                OUTPUT  s4, LCD_DATA
                CALL    LCD_E
                RETURN

LCD_PUTCMD:     CALL    LCD_RS_0
                CALL    LCD_RW_0
                OUTPUT  s4, LCD_DATA
                CALL    LCD_E
                RETURN

LCD_RS_0:       LOAD    s0, 00
                JUMP    LCD_RS
LCD_RS_1:       LOAD    s0, 01
LCD_RS:         OUTPUT  s0, LCD_CTRL_RS
                RETURN

LCD_RW_0:       LOAD    s0, 00
                JUMP    LCD_RW
LCD_RW_1:       LOAD    s0, 01
LCD_RW:         OUTPUT  s0, LCD_CTRL_RW
                RETURN

LCD_E:          LOAD    s0, 01
                OUTPUT  s0, LCD_CTRL_E
                CALL    DELAY1us
                LOAD    s0, 00
                OUTPUT  s0, LCD_CTRL_E
                CALL    DELAY1ms
                RETURN

; ------------------------------------------------------------------------------
; -- 時間待ち
; -- 50Mhz = 20ns
; -- 1命令 2クロック = 40ns
; -- s0,s1,s2,s3 
; -- 
; ------------------------------------------------------------------------------
DELAY1us:       LOAD    s0, 16
DELAY1us00:     SUB     s0, 01
                JUMP    NZ, DELAY1us00
                RETURN

DELAY100us:     LOAD    s1, 64
DELAY100us00:   CALL    DELAY1us
                SUB     s1, 01
                JUMP    NZ, DELAY100us00
                RETURN

DELAY1ms:       LOAD     s2, 0A
DELAY1ms00:     CALL    DELAY100us
                SUB     s2, 01
                JUMP    NZ, DELAY1ms00
                RETURN

DELAY20ms:      LOAD    s3, 14
DELAY20ms00:    CALL    DELAY1ms
                SUB     s3, 01
                JUMP    NZ, DELAY20ms00
                RETURN

; ------------------------------------------------------------------------------
; -- 割り込みルーチン
; -- 
; ------------------------------------------------------------------------------
INTERRUPT:
;               &lt;<コード>>
                RETURNI ENABLE

; ------------------------------------------------------------------------------
; -- 割り込みベクタ
; -- 
; ------------------------------------------------------------------------------
                ADDRESS 3FF
                JUMP    INTERRUPT

verilog のソース

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date:    10:28:22 04/24/2009 
// Design Name: 
// Module Name:    picolcd 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////
`default_nettype none

module picolcd (
    input   wire CLK_50MHZ,
    output  wire LCD_E,
    output  wire LCD_RS,
    output  wire LCD_RW,
    output  wire [7:0] LCD_DB,
    output  wire [7:0] LED
);

wire RESET;     // DCMのLOCKED_OUTをロジカルなリセットにするのがいい
wire DCM01_CLKIN_IBUF_OUT;
wire DCM01_CLK0_OUT;
wire DCM01_LOCKED_OUT;
wire [9:0]	CPU00_ADDRESS;
wire [17:0]	CPU00_INSTRUCTION;
wire [7:0]	CPU00_PORT_ID;
wire [7:0]	CPU00_IN_PORT;
wire [7:0]	CPU00_OUT_PORT;
wire CPU00_READ_STROBE;
wire CPU00_WRITE_STROBE;
wire CPU00_INTERRUPT;
wire CPU00_INTERRUPT_ACK;

generate_reset_pulse generate_reset_pulse (
    .clock(DCM01_CLKIN_IBUF_OUT),
    .reset_pulse(RESET)
);

dcm01 DCM01 (
	.CLKIN_IN(CLK_50MHZ), 
	.RST_IN(RESET), 
	.CLKIN_IBUFG_OUT(DCM01_CLKIN_IBUF_OUT), 
	.CLK0_OUT(DCM01_CLK0_OUT),
	.LOCKED_OUT(DCM01_LOCKED_OUT)
);

kcpsm3 CPU00 (
 	.address(CPU00_ADDRESS),
 	.instruction(CPU00_INSTRUCTION),
 	.port_id(CPU00_PORT_ID),
 	.write_strobe(CPU00_WRITE_STROBE),
 	.out_port(CPU00_OUT_PORT),
 	.read_strobe(CPU00_READ_STROBE),
 	.in_port(CPU00_IN_PORT),
 	.interrupt(CPU00_INTERRUPT),
 	.interrupt_ack(CPU00_INTERRUPT_ACK),
 	.reset(RESET),
 	.clk(DCM01_CLK0_OUT)
);

lcd CPU00ROM (
	.address(CPU00_ADDRESS),
	.instruction(CPU00_INSTRUCTION),
	.clk(DCM01_CLK0_OUT)
);

lcdport_out lcd_port_out (
    .we(CPU00_WRITE_STROBE),
    .port(CPU00_PORT_ID),
    .data(CPU00_OUT_PORT),
    .lcd_data(LCD_DB),
    .lcd_e(LCD_E),
    .lcd_rs(LCD_RS),
    .lcd_rw(LCD_RW),
    .led(LED)
);

endmodule

////////////////////////////////////////////////////////////////////////////////
//  Generate 1shot pulse 
////////////////////////////////////////////////////////////////////////////////
module generate_reset_pulse (
    input wire clock,
    output wire reset_pulse
);

SRL16 #(.INIT(16'h00FF)) SRL16_inst (
    .Q(reset_pulse),
    .A0(1'b1),
    .A1(1'b1),
    .A2(1'b1),
    .A3(1'b1),
    .CLK(clock),
    .D(1'b0)
);

endmodule

////////////////////////////////////////////////////////////////////////////////
//  CPU00(PicoBlaze)  ADDRESS DECODER
////////////////////////////////////////////////////////////////////////////////
module lcdport_out (
    input wire we,
    input wire [7:0] port,
    input wire [7:0] data,
    output reg [7:0] lcd_data,
    output reg lcd_e,
    output reg lcd_rs,
    output reg lcd_rw,
    output reg [7:0] led
);
always @(posedge we)
begin
    case(port)
        8'h01:	lcd_data = data;
        8'h02:	lcd_e  = data[0];
        8'h03:	lcd_rs = data[0];
        8'h04:	lcd_rw = data[0];
        8'h05:  led = data;
    endcase	
end

endmodule

PicoBlaze で LEDピコピコ

PicoBlaze で LEDピコピコです。

PicoBlaze については、 http://www.xilinx.com/picoblaze に関連する情報と
PicoBlaze のアセンブラなどのダウンロードがあります。

  • ロイヤリティフリーで使用可
  • 8ビットマイコン
  • 全ての命令は2クロックで実行される
  • 16個のレジスタ / SCRACH PAD RAM(64Byte) / ROM(1K x 18bit)

マイコンは、アセンブラのソース(.psm)を KCPSM3.EXE でアセンブルします。
エラーが無ければHDLのソース VHDL と Verilog のソースが出力されるので
これをトップモジュールに組み込めば動くという寸法です。

xilinx からダウンロード出来る KCPSM3.ZIP の中には資料含めて一式入ってるので
大丈夫でしょう。また ug129 のユーザーガイドには日本語版PDF(日付古いですが…)
があるので、これもダウンロードしておけばいいと思います。

アセンブラのソース

		ADDRESS	000

INIT00:
		ENABLE	INTERRUPT

MAIN00:		LOAD	s5, 00

MAIN90:
		OUTPUT	s5, 01    ; ポート1に出力する
		ADD	s5, 01
		CALL	DELAY1s
		JUMP	MAIN90


; ------------------------------------------------------------------------------
; -- 時間待ち
; -- 50Mhz = 20ns
; -- 1命令 2クロック = 40ns
; -- s0,s1,s2,s3 
; ------------------------------------------------------------------------------
DELAY1us:	LOAD	s0, 17
DELAY1us00:	SUB	s0, 01
		JUMP	NZ, DELAY1us00
		RETURN

DELAY100us:	LOAD	s1, 64
DELAY100us00:	CALL	DELAY1us
		SUB	s1, 01
		JUMP	NZ, DELAY100us00
		RETURN

DELAY1ms:	LOAD	s2, 0A
DELAY1ms00:	CALL	DELAY100us
		SUB	s2, 01
		JUMP	NZ, DELAY1ms00
		RETURN

DELAY20ms:	LOAD	s3, 14
DELAY20ms00:	CALL	DELAY1ms
		SUB	s3, 01
		JUMP	NZ, DELAY20ms00
		RETURN

DELAY1s:	LOAD	s4, 32
DELAY1s00:	CALL	DELAY20ms
		SUB	s4, 01
		JUMP	NZ, DELAY1s00
		RETURN

; ------------------------------------------------------------------------------
; -- 割り込みルーチン
; -- 
; ------------------------------------------------------------------------------
INTERRUPT:
;		&lt;<コード>>
		RETURNI ENABLE

; ------------------------------------------------------------------------------
; -- 割り込みベクタ
; -- 
; ------------------------------------------------------------------------------
		ADDRESS 3FF
		JUMP	INTERRUPT
</コード></code></pre>
<p>
Verilog のソース
</p>
<pre><code>
module main (
	input wire CLK_50MHZ,
	output wire [7:0] LED );

wire RESET;
wire CLKIN_IBUFG_OUT;
wire CLK0_OUT;
wire LOCKED_OUT;
wire 	[9:0]	address ;
wire 	[17:0]	instruction ;
wire 	[7:0]	port_id ;
wire 		write_strobe, read_strobe, interrupt_ack ;
wire 	[7:0]	out_port ;
wire 	[7:0]	in_port ;
wire		interrupt, reset, clk ;

user_reset reset00 (
	.CLOCK(),
	.RESET()
);

dcmsp00 dcmsp00 (
	.CLKIN_IN(CLK_50MHZ), 
	.RST_IN(RESET), 
	.CLKIN_IBUFG_OUT(CLKIN_IBUFG_OUT), 
	.CLK0_OUT(CLK0_OUT),
	.LOCKED_OUT(LOCKED_OUT)
);

kcpsm3 CPU00 (
 	.address(address),
 	.instruction(instruction),
 	.port_id(port_id),
 	.write_strobe(write_strobe),
 	.out_port(out_port),
 	.read_strobe(read_strobe),
 	.in_port(in_port),
 	.interrupt(interrupt),
 	.interrupt_ack(interrupt_ack),
 	.reset(RESET),
 	.clk(CLK0_OUT)
);

led CPU00LED (
	.address(address),
	.instruction(instruction),
	.clk(CLK0_OUT)
);

outp_sel outp_sel (
	.port_id(port_id),
	.write_strobe(write_strobe),
	.indata(out_port),
	.otdata(LED)
);

endmodule

//
// Generate RESET PULSE
//
module user_reset (
	input wire CLOCK,
	output wire RESET );

   
   SRL16 #(.INIT(16'h00FF))	// Initial Value of Shift Register
     SRL16_inst (
      .Q(RESET),			// SRL data output
      .A0(1'b1),			// Select[0] input
      .A1(1'b1),			// Select[1] input
      .A2(1'b1),			// Select[2] input
      .A3(1'b1),			// Select[3] input
      .CLK(CLOCK),		// Clock input
      .D(1'b0)				// SRL data input
   );

   // End of SRL16_inst instantiation
endmodule

module outp_sel (
	input wire port_id,
	input wire write_strobe,
	input wire [7:0] indata,
	output reg [7:0] otdata
);

	always @(posedge write_strobe)
		begin
			if(port_id == 1)
				otdata = indata;
		end		

endmodule