とりあえず、FPGA版 Hello, World

普段 DWMとか読まないんで、CPLDやFPGAの事情はさっぱりわかりません。昔のページに XILINX XC9500シリーズの
CPLD使った記事も書きましたが2002年6月のことで、当然ここのサイトには掲載してません。
今更という感じはあるのですが、久しぶりにやってみたくなったので入門キットを買ってボチボチ遊びたいと思います。

FPGAの「Hello, World」は、はやり LEDピコピコですが、今回は飛ばして VGA 出力の練習です。
Verilog HDL入門本見ながら書いてみました。以下ソースとUCFです。
Webpack 10.1で作成してとりあえず成功っと


モニタはPCデポ店頭に積んであったセリング KW-M80C というとこのSVGAモニタでアナログRGBとコンポッジト入力だけの
シンプル構成です。1万4千円くらいだったと思います。まぁちょっと使う程度なら使えるモニタです。

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date:    12:46:33 06/22/2008
// Design Name:
// Module Name:    vga
// Project Name:
// Target Devices:
// Tool versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
//
// -- 水平タイミング --
// 表示期間:   640ドット
// フロントポーチ:  16ドット
// 水平同期:    96ドット(656)
// バックポーチ:   48ドット(752)
//
// -- 垂直タイミング --
// 表示期間:   480ライン
// フロントポーチ:  10ライン
// 垂直同期:     2ライン(490)
// バックポーチ:   29ライン(492)

module vga(CLK_50MHZ, VGA_R, VGA_G, VGA_B, VGA_HSYNC, VGA_VSYNC);

input CLK_50MHZ;
output [3:0] VGA_R;
output [3:0] VGA_G;
output [3:0] VGA_B;
output VGA_HSYNC;
output VGA_VSYNC;

reg [3:0] VGA_R;
reg [3:0] VGA_G;
reg [3:0] VGA_B;
reg VGA_HSYNC;
reg VGA_VSYNC;

wire clk_25mhz;
reg clk_25mhz_a;
assign clk_25mhz = clk_25mhz_a;

reg [9:0] hcount;
reg [9:0] vcount;

/* 25MHz のクロック作成 */
always @(posedge CLK_50MHZ)
begin
clk_25mhz_a <= ‾clk_25mhz_a;
end

/* 25Mhz のクロックで画面描画 */
always @(posedge clk_25mhz)
begin

// 水平タイミング
hcount <= hcount + 1;     // 水平カウント 800 で一周     if(hcount > 10'd799)
begin
hcount <= 0;
vcount <= vcount + 1;
end
// 水平同期出力
if(hcount == 10'd655)
begin
VGA_HSYNC <= 0;
VGA_R <= 4'b0000;
VGA_G <= 4'b0000;
VGA_B <= 4'b0000;
end
// 水平同期出力終了
if(hcount == 10'd751)
begin
VGA_HSYNC <= 1;     end     // 垂直タイミング     // 521 ラインで一周     if(vcount > 10'd520)
begin
vcount <= 0;
end
// 垂直同期開始
if(vcount == 10'd489)
begin
VGA_VSYNC <= 0;
end
// 垂直同期終了
if(vcount == 10'd491)
begin
VGA_VSYNC <= 1;
end

// カラーバーもどき → メモリインタフェースにする
if(hcount == 10'd0)
begin
VGA_R <= 4'b1111;
VGA_G <= 4'b1111;
VGA_B <= 4'b1111;
end
if(hcount == 10'd79)
begin
VGA_R <= 4'b0000;
VGA_G <= 4'b1111;
VGA_B <= 4'b1111;
end
if(hcount == 10'd159)
begin
VGA_R <= 4'b1111;
VGA_G <= 4'b0000;
VGA_B <= 4'b1111;
end
if(hcount == 10'd239)
begin
VGA_R <= 4'b1111;
VGA_G <= 4'b1111;
VGA_B <= 4'b0000;
end
if(hcount == 10'd319)
begin
VGA_R <= 4'b1111;
VGA_G <= 4'b0000;
VGA_B <= 4'b0000;
end
if(hcount == 10'd399)
begin
VGA_R <= 4'b0000;
VGA_G <= 4'b1111;
VGA_B <= 4'b0000;
end
if(hcount == 10'd479)
begin
VGA_R <= 4'b0000;
VGA_G <= 4'b0000;
VGA_B <= 4'b1111;
end
if(hcount == 10'd559)
begin
VGA_R <= 4'b0000;
VGA_G <= 4'b0000;
VGA_B <= 4'b0000;
end

end
endmodule

 
<pre><code>

NET "VGA_R&lt;3&gt;" LOC = "C8" | IOSTANDARD = LVTTL | DRIVE = 8 | SLEW = FAST ;

NET "VGA_R&lt;2&gt;" LOC = "B8" | IOSTANDARD = LVTTL | DRIVE = 8 | SLEW = FAST ;

NET "VGA_R&lt;1&gt;" LOC = "B3" | IOSTANDARD = LVTTL | DRIVE = 8 | SLEW = FAST ;

NET "VGA_R&lt;0&gt;" LOC = "A3" | IOSTANDARD = LVTTL | DRIVE = 8 | SLEW = FAST ;

NET "VGA_G&lt;3&gt;" LOC = "D6" | IOSTANDARD = LVTTL | DRIVE = 8 | SLEW = FAST ;

NET "VGA_G&lt;2&gt;" LOC = "C6" | IOSTANDARD = LVTTL | DRIVE = 8 | SLEW = FAST ;

NET "VGA_G&lt;1&gt;" LOC = "D5" | IOSTANDARD = LVTTL | DRIVE = 8 | SLEW = FAST ;

NET "VGA_G&lt;0&gt;" LOC = "C5" | IOSTANDARD = LVTTL | DRIVE = 8 | SLEW = FAST ;

NET "VGA_B&lt;3&gt;" LOC = "C9" | IOSTANDARD = LVTTL | DRIVE = 8 | SLEW = FAST ;

NET "VGA_B&lt;2&gt;" LOC = "B9" | IOSTANDARD = LVTTL | DRIVE = 8 | SLEW = FAST ;

NET "VGA_B&lt;1&gt;" LOC = "D7" | IOSTANDARD = LVTTL | DRIVE = 8 | SLEW = FAST ;

NET "VGA_B&lt;0&gt;" LOC = "C7" | IOSTANDARD = LVTTL | DRIVE = 8 | SLEW = FAST ;

NET "VGA_HSYNC" LOC = "C11" | IOSTANDARD = LVTTL | DRIVE = 8 | SLEW = FAST ;

NET "VGA_VSYNC" LOC = "B11" | IOSTANDARD = LVTTL | DRIVE = 8 | SLEW = FAST ;

NET "CLK_50MHZ" LOC = "E12"| IOSTANDARD = LVCMOS33 ;

# NET "CLK_AUX" LOC = "V12"| IOSTANDARD = LVCMOS33 ;

# NET "CLK_SMA" LOC = "U12"| IOSTANDARD = LVCMOS33 ;


 

Spartan-3A DCM を veritak で動作確認してみた。

Spartan-3A スターターキットの PDF の資料やアプリケーションノートなど見てますが
基本クロックは、50MHzの水晶が原発ですが、それ以外の
クロックが欲しいときは、DCM(Digital Clock Manager)を使用するようです。
ということで、DCM を使用する準備として veritak で確認してみました。

  • CORE Generator で DCM のコードを GUI で生成
  • テスト用の Verilog コードを書いてみる
  • veritak(シェアウェア版)でシミューレーションしてみる

CORE Generator を起動して DCM のコアを作成してみました。
今回は、 FPGA Features and Design -> Clocking -> Spartan-3E, Spartan-3A -> Single DCM_SP
とファンクションを選択して、DCM作成です。
今回は、CLK0以外に、90度づつ位相ずれた CLK90, CLK180, CLK270 なども
出力するようにしてみました。

また、PDF 文書にあった推奨リセット回路(シフトレジスタでワンショットパルスを出す)も追加しました。

生成された DCM モジュール

////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 1995-2008 Xilinx, Inc.  All rights reserved.
////////////////////////////////////////////////////////////////////////////////
//   ____  ____ 
//  /   /\/   / 
// /___/  \  /    Vendor: Xilinx 
// \   \   \/     Version : 10.1.03
//  \   \         Application : xaw2verilog
//  /   /         Filename : dcm_test01.v
// /___/   /\     Timestamp : 04/21/2009 13:34:03
// \   \  /  \ 
//  \___\/\___\ 
//
//Command: xaw2verilog -st C:\mkusunoki\\dcm_test01.xaw C:\mkusunoki\\dcm_test01
//Design Name: dcm_test01
//Device: xc3s700a-4fg484
//
// Module dcm_test01
// Generated by Xilinx Architecture Wizard
// Written for synthesis tool: XST
`timescale 1ns / 1ps

module dcm_test01(CLKFB_IN, 
                  CLKIN_IN, 
                  RST_IN, 
                  CLKIN_IBUFG_OUT, 
                  CLK0_OUT, 
                  CLK90_OUT, 
                  CLK180_OUT, 
                  CLK270_OUT, 
                  LOCKED_OUT);

    input CLKFB_IN;
    input CLKIN_IN;
    input RST_IN;
   output CLKIN_IBUFG_OUT;
   output CLK0_OUT;
   output CLK90_OUT;
   output CLK180_OUT;
   output CLK270_OUT;
   output LOCKED_OUT;
   
   wire CLKFB_IBUFG;
   wire CLKIN_IBUFG;
   wire CLK0_BUF;
   wire CLK90_BUF;
   wire CLK180_BUF;
   wire CLK270_BUF;
   wire GND_BIT;
   
   assign GND_BIT = 0;
   assign CLKIN_IBUFG_OUT = CLKIN_IBUFG;
   IBUFG CLKFB_IBUFG_INST (.I(CLKFB_IN), 
                           .O(CLKFB_IBUFG));
   IBUFG CLKIN_IBUFG_INST (.I(CLKIN_IN), 
                           .O(CLKIN_IBUFG));
   BUFG CLK0_BUFG_INST (.I(CLK0_BUF), 
                        .O(CLK0_OUT));
   BUFG CLK90_BUFG_INST (.I(CLK90_BUF), 
                         .O(CLK90_OUT));
   BUFG CLK180_BUFG_INST (.I(CLK180_BUF), 
                          .O(CLK180_OUT));
   BUFG CLK270_BUFG_INST (.I(CLK270_BUF), 
                          .O(CLK270_OUT));
   DCM_SP DCM_SP_INST (.CLKFB(CLKFB_IBUFG), 
                       .CLKIN(CLKIN_IBUFG), 
                       .DSSEN(GND_BIT), 
                       .PSCLK(GND_BIT), 
                       .PSEN(GND_BIT), 
                       .PSINCDEC(GND_BIT), 
                       .RST(RST_IN), 
                       .CLKDV(), 
                       .CLKFX(), 
                       .CLKFX180(), 
                       .CLK0(CLK0_BUF), 
                       .CLK2X(), 
                       .CLK2X180(), 
                       .CLK90(CLK90_BUF), 
                       .CLK180(CLK180_BUF), 
                       .CLK270(CLK270_BUF), 
                       .LOCKED(LOCKED_OUT), 
                       .PSDONE(), 
                       .STATUS());
   defparam DCM_SP_INST.CLK_FEEDBACK = "1X";
   defparam DCM_SP_INST.CLKDV_DIVIDE = 2.0;
   defparam DCM_SP_INST.CLKFX_DIVIDE = 1;
   defparam DCM_SP_INST.CLKFX_MULTIPLY = 4;
   defparam DCM_SP_INST.CLKIN_DIVIDE_BY_2 = "FALSE";
   defparam DCM_SP_INST.CLKIN_PERIOD = 20.000;
   defparam DCM_SP_INST.CLKOUT_PHASE_SHIFT = "NONE";
   defparam DCM_SP_INST.DESKEW_ADJUST = "SYSTEM_SYNCHRONOUS";
   defparam DCM_SP_INST.DFS_FREQUENCY_MODE = "LOW";
   defparam DCM_SP_INST.DLL_FREQUENCY_MODE = "LOW";
   defparam DCM_SP_INST.DUTY_CYCLE_CORRECTION = "TRUE";
   defparam DCM_SP_INST.FACTORY_JF = 16'hC080;
   defparam DCM_SP_INST.PHASE_SHIFT = 0;
   defparam DCM_SP_INST.STARTUP_WAIT = "FALSE";
endmodule

作成してみたテストベンチ

`default_nettype none
`timescale 1ns / 1ns

module dcm_test01_testbench;

reg CLK50MHz;
reg RST_IN;
wire CLKIN_IBUFG_OUT;
wire CLK0_OUT;
wire CLK90_OUT;
wire CLK180_OUT;
wire CLK270_OUT;
wire LOCKED_OUT;

dcm_test01 dcm01 (
	.CLKFB_IN(CLK0_OUT),
	.CLKIN_IN(CLK50MHz),
	.RST_IN(Q),
	.CLKIN_IBUFG_OUT(CLKIN_IBUFG_OUT),
	.CLK0_OUT(CLK0_OUT),
	.CLK90_OUT(CLK90_OUT),
	.CLK180_OUT(CLK180_OUT),
	.CLK270_OUT(CLK270_OUT),
	.LOCKED_OUT(LOCKED_OUT)
	);

wire Q;
//defparam U1.INIT = 16'h000F;
SRL16 #(16'h000F) U1 (
	.Q(Q),
	.A0(1),
	.A1(1),
	.A2(1),
	.A3(1),
	.CLK(CLK50MHz),
	.D(0)
);

// クロック
initial
begin
	CLK50MHz = 0;
	forever #10 CLK50MHz = ~CLK50MHz;
end

initial
begin
	#1600
	$finish;
end

always
	$monitor($time, "clkin=%b reset=%b clk0=%b clk90=%b clk180=%b clk270=%b locked=%b\n", CLK50MHz, RST_IN,CLK0_OUT,CLK90_OUT,CLK180_OUT,CLK270_OUT,LOCKED_OUT);
endmodule

で、veritakでシミューレーションしてみましたところ、各種クロックと、リセットに反応すること、と
クロックのロック状態も表示されたので実際にインプリする場合もちょっと安心ということで

にしても、ついでで DDR2 SDRAM も MIG で作成してみて veritak してみましたところ
これも動きそうな感じではあります。が、UG086 に書いてる使いかたが
まだよく理解出来ないのと、DDR/DDR2 な SDRAM の基本もわかってないので
これから余裕が出来れば、使ってみたいですね。
とりあえず、picoblaze を先に使用したいです。