1
0
mirror of https://github.com/pConst/basic_verilog.git synced 2025-01-28 07:02:55 +08:00
Konstantin Pavlov (pt) 40533743d7 Added altera cookbook
2015-12-15 22:44:58 +03:00

356 lines
8.8 KiB
Verilog

// Copyright 2008 Altera Corporation. All rights reserved.
// Altera products are protected under numerous U.S. and foreign patents,
// maskwork rights, copyrights and other intellectual property laws.
//
// This reference design file, and your use thereof, is subject to and governed
// by the terms and conditions of the applicable Altera Reference Design
// License Agreement (either as signed by you or found at www.altera.com). By
// using this reference design file, you indicate your acceptance of such terms
// and conditions between you and Altera Corporation. In the event that you do
// not agree with such terms and conditions, you may not use the reference
// design file and please promptly destroy any copies you have made.
//
// This reference design file is being provided on an "as-is" basis and as an
// accommodation and therefore all warranties, representations or guarantees of
// any kind (whether express, implied or statutory) including, without
// limitation, warranties of merchantability, non-infringement, or fitness for
// a particular purpose, are specifically disclaimed. By making this reference
// design file available, Altera expressly does not recommend, suggest or
// require that this reference design file be used in combination with any
// other product not provided by Altera.
/////////////////////////////////////////////////////////////////////////////
// baeckler - 04-29-2008
//
// LCD driver, 2 rows of 16 chars.
// e.g. Crystalfontz displays on Altera DE / Nios series boards.
//
module asc_to_lcd (
clk,rst,
lcd_data,
lcd_rnw,
lcd_en,
lcd_rs,
disp_text
);
`include "log2.inc"
input clk,rst;
output reg [7:0] lcd_data;
output lcd_rnw;
output reg lcd_en;
output reg lcd_rs;
input [32*8-1:0] disp_text;
parameter CLOCK_MHZ = 50;
// The "busy" flag doesn't seem to work
// reliably. Definitely a headache meeting
// the tristate timing. This uses internal
// timer instead of asking the display.
assign lcd_rnw = 1'b0;
////////////////////////////////////////////
// Generate enable pulse at microsecond interval
////////////////////////////////////////////
localparam CLOCK_HZ = CLOCK_MHZ * 1_000_000;
localparam TICKS_PER_US = CLOCK_MHZ;
localparam US_CNTR_BITS = log2(TICKS_PER_US-1);
reg [US_CNTR_BITS-1:0] us_cntr;
reg us_pulse;
always @(posedge clk) begin
if (rst) begin
us_pulse <= 1'b0;
us_cntr <= 0;
end
else begin
if (us_pulse) begin
us_cntr <= 0;
us_pulse <= 0;
end
else begin
us_cntr <= us_cntr + 1'b1;
if (us_cntr == (TICKS_PER_US-2)) us_pulse <= 1'b1;
end
end
end
/////////////////////////////////////////////////
// Generate enable pulse at 50ns interval (or a bit more)
/////////////////////////////////////////////////
localparam APPROX_TICKS_PER_50_NS = 50 * CLOCK_MHZ / 1000;
localparam TICKS_PER_50_NS =
((APPROX_TICKS_PER_50_NS * 1000 / CLOCK_MHZ) < 50) ?
APPROX_TICKS_PER_50_NS + 1 :
APPROX_TICKS_PER_50_NS;
localparam NS_CNTR_BITS = log2(TICKS_PER_50_NS-1);
reg ns_pulse;
generate
if (TICKS_PER_50_NS == 1) begin
always @(*) ns_pulse = 1'b1;
end
else if (TICKS_PER_50_NS == 2) begin
always @(posedge clk) begin
if (rst) ns_pulse <= 1'b0;
else ns_pulse <= ~ns_pulse;
end
end
else begin
reg [NS_CNTR_BITS-1:0] ns_cntr;
always @(posedge clk) begin
if (rst) begin
ns_pulse <= 1'b0;
ns_cntr <= 0;
end
else begin
if (ns_pulse) begin
ns_cntr <= 0;
ns_pulse <= 0;
end
else begin
ns_cntr <= ns_cntr + 1'b1;
if (ns_cntr == (TICKS_PER_50_NS-2)) ns_pulse <= 1'b1;
end
end
end
end
endgenerate
/////////////////////////////////////////////////
// Character select
/////////////////////////////////////////////////
reg [4:0] char_ptr; // 0 = line 1 leftmost, 16 = line 2 leftmost
wire [7:0] char_mux_w;
reg [7:0] char_mux;
genvar i,k;
generate
for (k=0;k<8;k=k+1)
begin : out
wire [31:0] tmp;
for (i=0;i<32;i=i+1)
begin : mx
assign tmp [i] = disp_text[k+(31-i)*8];
end
assign char_mux_w[k] = tmp[char_ptr];
end
endgenerate
// the select happens to be stable for more than one cycle
// before the data is needed. Register it.
always @(posedge clk) begin
if (rst) char_mux <= 0;
else char_mux <= char_mux_w;
end
/////////////////////////////////////////////////
// Control machine
/////////////////////////////////////////////////
reg [14:0] timer;
reg timer_expire;
reg init_complete;
reg [4:0] state /* synthesis preserve */;
reg [4:0] return_state;
parameter
ST_INIT = 5'd0,
ST_INIT1 = 5'd1,
ST_INIT2 = 5'd2,
ST_INIT3 = 5'd3,
ST_INIT4 = 5'd4,
ST_INIT5 = 5'd5,
ST_INIT6 = 5'd6,
ST_INIT7 = 5'd7,
ST_INIT8 = 5'd8,
ST_INIT9 = 5'd9,
ST_INIT10 = 5'd10,
ST_INIT11 = 5'd11,
ST_INIT12 = 5'd12,
ST_INIT13 = 5'd13,
ST_INIT14 = 5'd14,
ST_LINE_ONE = 5'd15,
ST_LINE_ONE_CHARS = 5'd16,
ST_LINE_TWO = 5'd17,
ST_LINE_TWO_CHARS = 5'd18,
ST_WRITE = 5'd19,
ST_WRITE1 = 5'd20,
ST_WRITE2 = 5'd21,
ST_WRITE3 = 5'd22,
ST_WRITE4 = 5'd23,
ST_WRITE5 = 5'd24,
ST_WRITE6 = 5'd25,
ST_WRITE7 = 5'd26,
ST_WRITE8 = 5'd27,
ST_WRITE9 = 5'd28,
ST_BUSY_WAIT = 5'd29,
ST_BUSY_WAIT1 = 5'd30,
ST_BUSY_WAIT2 = 5'd31;
always @(posedge clk) begin
if (rst) begin
timer <= 0;
timer_expire <= 0;
state <= ST_INIT;
lcd_data <= 8'h0;
lcd_rs <= 1'b0;
lcd_en <= 1'b0;
return_state <= 0;
char_ptr <= 5'b0;
init_complete <= 1'b0;
end
else begin
// rough timer for init phase
timer_expire <= (~|timer);
if (!timer_expire) timer <= timer - us_pulse;
case (state)
// wait 15ms then write
ST_INIT : begin
timer <= 14'h3a98; // 15ms
state <= ST_INIT1;
end
ST_INIT1 : begin
state <= ST_INIT2;
lcd_data <= 8'h38;
lcd_rs <= 1'b0;
end
ST_INIT2 : if (timer_expire) state <= ST_INIT3;
ST_INIT3 : begin
// first init write
return_state <= ST_INIT4;
state <= ST_WRITE;
end
// wait 4.1ms then write
ST_INIT4 : begin
timer <= 14'h1004; // 4.1ms
state <= ST_INIT5;
end
ST_INIT5 : state <= ST_INIT6;
ST_INIT6 : if (timer_expire) state <= ST_INIT7;
ST_INIT7 : begin
// second init write
return_state <= ST_INIT8;
state <= ST_WRITE;
end
// wait 100 us then write
ST_INIT8 : begin
timer <= 14'h0064; // 100us
state <= ST_INIT9;
end
ST_INIT9 : state <= ST_INIT10;
ST_INIT10 : if (timer_expire) state <= ST_INIT11;
ST_INIT11 : begin
// second init write
return_state <= ST_INIT12;
state <= ST_WRITE;
end
// finish up initial settings
ST_INIT12 : begin
lcd_data <= 8'hc; // display on
return_state <= ST_INIT13;
state <= ST_WRITE;
end
ST_INIT13 : begin
lcd_data <= 8'h1; // display clr
return_state <= ST_INIT14;
state <= ST_WRITE;
end
ST_INIT14 : begin
lcd_data <= 8'h6; // cursor inc
return_state <= ST_LINE_ONE;
state <= ST_WRITE;
end
ST_LINE_ONE : begin
init_complete <= 1'b1;
lcd_data <= 8'h80; // line 1
lcd_rs <= 1'b0;
char_ptr <= 0;
return_state <= ST_LINE_ONE_CHARS;
state <= ST_WRITE;
end
ST_LINE_ONE_CHARS : begin
lcd_rs <= 1'b1;
lcd_data <= char_mux;
if (char_ptr[3:0] == 4'd15)
return_state <= ST_LINE_TWO;
char_ptr <= char_ptr + 1'b1;
state <= ST_WRITE;
end
ST_LINE_TWO : begin
lcd_data <= 8'hc0; // line 2
lcd_rs <= 1'b0;
return_state <= ST_LINE_TWO_CHARS;
state <= ST_WRITE;
end
ST_LINE_TWO_CHARS : begin
lcd_rs <= 1'b1;
lcd_data <= char_mux;
if (char_ptr[3:0] == 4'd15)
return_state <= ST_LINE_ONE;
char_ptr <= char_ptr + 1'b1;
state <= ST_WRITE;
end
// subroutine : write a character / cmd to LCD
ST_WRITE : begin
// align with 50ns pulse
if (ns_pulse) state <= ST_WRITE1;
end
ST_WRITE1 : begin
// Block 1 : satisfy data setup
if (ns_pulse) begin
lcd_en <= 1'b1;
state <= ST_WRITE2;
end
end
// blocks 2..6 ENA active
ST_WRITE2 : if (ns_pulse) state <= ST_WRITE3;
ST_WRITE3 : if (ns_pulse) state <= ST_WRITE4;
ST_WRITE4 : if (ns_pulse) state <= ST_WRITE5;
ST_WRITE5 : if (ns_pulse) state <= ST_WRITE6;
ST_WRITE6 : begin
if (ns_pulse) begin
state <= ST_WRITE7;
lcd_en <= 1'b0;
end
end
// block 7 : satisfy data hold
ST_WRITE7 : if (ns_pulse) state <= ST_WRITE8;
// blocks 8 and 9 - satisfy ena cycle time
ST_WRITE8 : if (ns_pulse) state <= ST_WRITE9;
ST_WRITE9 : begin
if (ns_pulse) begin
state <= ST_BUSY_WAIT;
end
end
ST_BUSY_WAIT : begin
if (!init_complete)
timer <= 14'h05dc; // 1.5ms
else
timer <= 14'h002b; // 43us
state <= ST_BUSY_WAIT1;
end
ST_BUSY_WAIT1 : state <= ST_BUSY_WAIT2;
ST_BUSY_WAIT2 : if (timer_expire) state <= return_state;
endcase
end
end
endmodule