昨日の続きです。昨日はV850の制御ソフトだったので今日はFPGA側のRTLです。
メインモジュールは、4つのサブモジュールを結合しています。
- clkgenは基本クロックを分周してlcd用の2.3MHzを作っています
- spiはSPIの受信と書き込み用アドレスの発生やコマンドの処理をします
- dpramはデュアルポートメモリで9k byteのvramです。
- lcdは鈴商液晶の制御と読み込み用のアドレスを発生します。
`default_nettype none
module main (
output [3:0] lcd_d,
output cl1, cl2, frm, lcd_m,
// output [4:0] ctrl,
output led,
input cs, rs, sck, mosi,
input iclk);
wire [4:0] ctrl;
wire [13:0] adr;
wire we;
wire clk, lcd_clk;
wire [13:0] lcd_adr;
wire [7:0] ram_out, spi_out;
assign led = 1'b0;
// clkgenインスタンス
clkgen cg (
.CLKIN_IN(iclk),
.RST_IN(1'b0),
.CLKDV_OUT(lcd_clk),
.CLKIN_IBUFG_OUT(),
.CLK0_OUT(clk),
.LOCKED_OUT());
// SPI
spi spi0 (
.adr(adr),
.out(spi_out),
.ctrl(ctrl),
.we(we),
.cs(cs),
.rs(rs),
.sck(sck),
.in(mosi),
.clk(clk));
// dpramインスタンス portA: SPI, portB:LCD
dpram ram0 (
.addra(adr),
.addrb(lcd_adr),
.clka(clk),
.clkb(clk),
.dina(spi_out),
.doutb(ram_out),
.wea(we));
// 鈴商LCDインスタンス
lcd lcd0 (
.adr(lcd_adr),
.in(ram_out),
.d(lcd_d),
.cl1(cl1),
.cl2(cl2),
.frm(frm),
.m(lcd_m),
.clk(lcd_clk));
endmodule
clkgenとdpramはcore generatorを使って作りました。基本的な使い方は
前回と一緒ですが、作るものが違います。
SPIの受信とコマンドの実行を行うspiモジュールです(spi.v)
`default_nettype none
module spi (
output reg [13:0] adr = 14'h0, // ramアドレス
output reg [7:0] out, // ramへの書き込みデータ
output reg we = 1'b0, // ramへの書き込み信号 1:書き込み
output reg [4:0] ctrl = 5'h18, // lcd制御 4:pwr 3:dsp 2-0:コントラスト
input cs, // 1:disable 0:enable
input rs, // 0:command 1:data
input sck, // 立ち上がりでデータ取得
input in, // 入力データ
input clk);
reg [1:0] ck = 2'b00; // sck立ち上がり検知用シフトレジスタ
reg d; // 入力ラッチ
reg [2:0] ctr = 0; // 入力bit数カウンタ
reg s = 1'b0; // 1:2byte目
reg [5:0] adrh; // adrの上位記憶用
wire [7:0] data = {out[6:0], d};
always @(posedge clk) begin
ck <= {ck, sck};
d <= in;
if(cs) begin ctr <= 0; s <= 1'b0; end
if(we) begin we <= 1'b0; adr <= adr == 14'h23ff ? 14'h0000 : adr + 1; end
if(~cs & ck == 2'b01) begin // 立ち上がり発見
out <= data;
ctr <= ctr + 1;
if(ctr == 7) begin
if(rs) we <= 1'b1;
else if(s) begin s <= 1'b0; adr <= {adrh, data}; end
else if(data[7]) begin s <= 1'b1; adrh <= data[5:0]; end
else if(data[6]) ctrl <= data[4:0];
end
end
end
endmodule
sckの立ち上がりを見つけてシフトレジスタにためていって、8個たまったら、RAMへ書き込むかコマンドの実行をします。
鈴商LCDの制御です。
前回と較べてクロックの分周を外に出したので少し簡単になっています。
`default_nettype none
module lcd (
// ram interface
output reg [13:0] adr = 14'h002f,
input [7:0] in,
// lcd interface
output [3:0] d,
output reg cl1 = 1'b0,
output reg cl2 = 1'b1,
output reg frm = 1'b1,
output reg m = 1'b1,
// clock
input clk);
reg [6:0] x = 0;
reg [7:0] y = 0;
assign d = x[0] ? in[7:4] : in[3:0];
// cl1, cl2
always @(posedge clk) begin
cl2 <= ~cl2;
cl1 <= cl2 & x == 99;
end
// x, y, d, frame, m
always @(posedge clk)
if(~cl2) begin
x <= x == 99 ? 0 : x + 1;
if(x == 99) begin
y <= y == 191 ? 0 : y + 1;
if(y == 191) begin frm <= 1'b1; m <= ~m; end
if(y == 0) frm <= 1'b0;
end
end
// adr
always @(posedge clk)
if(~cl2)
if(x == 99) adr <= y == 191 ? 14'h002f : adr + 97;
else if(x[0]) adr <= adr - 1;
endmodule
最後はucfファイルです。
NET "iclk" LOC = "P88" ;
NET "led" LOC = "P98" ;
NET "cs" LOC = "P49" ;
NET "sck" LOC = "P47" ;
NET "mosi" LOC = "P48" ;
NET "rs" LOC = "P40" ;
NET "cl1" LOC = "P60" ; # A03
NET "cl2" LOC = "P61" ; # B03
NET "frm" LOC = "P62" ; # A04
NET "lcd_m" LOC = "P63" ; # B04
NET "lcd_d<3>" LOC = "P53" ; # A01
NET "lcd_d<2>" LOC = "P54" ; # B01
NET "lcd_d<1>" LOC = "P57" ; # A02
NET "lcd_d<0>" LOC = "P58" ; # B02
一応、LCDのパワー制御用のコマンドもこっそり作っていますが、まだ試していません。
なんかblogにソースを貼るのが面倒になってきました(笑)
ここはpreタグの中でも<とかが化けるので&lt;に変換してから貼らないといけません。
zipで固めてダウンロードしてみたいなのは、blogじゃ駄目なんでしょうか?
よそにダウンロード専用のホームページとか作るのかな?goo以外のblogだとできたりするんでしょうか?
だんだん手抜きっぽくなってきてますが、RTLはこんな感じです。