1
0
mirror of git://projects.qi-hardware.com/nn-usb-fpga.git synced 2025-04-21 12:27:27 +03:00

Updating examples to Board changes, adding irq driver demo

This commit is contained in:
Carlos Camargo
2010-06-11 08:06:13 -05:00
parent c8b70e5307
commit 5041c0eb60
41 changed files with 2263 additions and 145 deletions

View File

@@ -0,0 +1,232 @@
/*
* PS2 Interface
* Copyright (C) 2009 Takeshi Matsuya
* Copyright (C) 2007, 2008, 2009 Sebastien Bourdeauducq
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
module ps2 #(
parameter csr_addr = 4'h0,
parameter clk_freq = 100000000
) (
input sys_rst,
input sys_clk,
input [13:0] csr_a,
input csr_we,
input [31:0] csr_di,
output reg [31:0] csr_do,
inout ps2_clk,
inout ps2_data,
output reg irq
);
/* CSR interface */
wire csr_selected = csr_a[13:10] == csr_addr;
reg tx_busy;
//-----------------------------------------------------------------
// divisor
//-----------------------------------------------------------------
reg [9:0] enable_counter;
wire enable;
assign enable = (enable_counter == 10'd0);
parameter divisor = clk_freq/12800/16;
always @(posedge sys_clk) begin
if(sys_rst)
enable_counter <= divisor - 10'd1;
else begin
enable_counter <= enable_counter - 10'd1;
if(enable)
enable_counter <= divisor - 10'd1;
end
end
//-----------------------------------------------------------------
// Synchronize ps2 clock and data
//-----------------------------------------------------------------
reg ps2_clk_1;
reg ps2_data_1;
reg ps2_clk_2;
reg ps2_data_2;
reg ps2_clk_out;
reg ps2_data_out1, ps2_data_out2;
always @(posedge sys_clk) begin
ps2_clk_1 <= ps2_clk;
ps2_data_1 <= ps2_data;
ps2_clk_2 <= ps2_clk_1;
ps2_data_2 <= ps2_data_1;
end
/* PS2 */
reg [7:0] kcode;
reg rx_clk_data;
reg [5:0] rx_clk_count;
reg [4:0] rx_bitcount;
reg [10:0] rx_data;
reg [10:0] tx_data;
reg we_reg;
/* FSM */
reg [2:0] state;
reg [2:0] next_state;
parameter RECEIVE = 3'd0;
parameter WAIT_READY = 3'd1;
parameter CLOCK_LOW = 3'd2;
parameter CLOCK_HIGH = 3'd3;
parameter CLOCK_HIGH1 = 3'd4;
parameter CLOCK_HIGH2 = 3'd5;
parameter WAIT_CLOCK_LOW = 3'd6;
parameter TRANSMIT = 3'd7;
assign state_receive = state == RECEIVE;
assign state_transmit = state == TRANSMIT;
always @(posedge sys_clk) begin
if(sys_rst)
state = RECEIVE;
else begin
state = next_state;
end
end
/* ps2 clock falling edge 100us counter */
//parameter divisor_100us = clk_freq/10000;
parameter divisor_100us = 1;
reg [16:0] watchdog_timer;
wire watchdog_timer_done;
assign watchdog_timer_done = (watchdog_timer == 17'd0);
always @(sys_clk) begin
if(sys_rst||ps2_clk_out)
watchdog_timer <= divisor_100us - 1;
else if(~watchdog_timer_done)
watchdog_timer <= watchdog_timer - 1;
end
always @(*) begin
ps2_clk_out = 1'b1;
ps2_data_out1 = 1'b1;
tx_busy = 1'b1;
next_state = state;
case(state)
RECEIVE: begin
tx_busy = 1'b0;
if(we_reg) begin
next_state = WAIT_READY;
end
end
WAIT_READY: begin
if(rx_bitcount == 5'd0) begin
ps2_clk_out = 1'b0;
next_state = CLOCK_LOW;
end
end
CLOCK_LOW: begin
ps2_clk_out = 1'b0;
if(watchdog_timer_done) begin
next_state = CLOCK_HIGH;
end
end
CLOCK_HIGH: begin
next_state = CLOCK_HIGH1;
end
CLOCK_HIGH1: begin
next_state = CLOCK_HIGH2;
end
CLOCK_HIGH2: begin
ps2_data_out1 = 1'b0;
next_state = WAIT_CLOCK_LOW;
end
WAIT_CLOCK_LOW: begin
ps2_data_out1 = 1'b0;
if(ps2_clk_2 == 1'b0) begin
next_state = TRANSMIT;
end
end
TRANSMIT: begin
if(rx_bitcount == 5'd10) begin
next_state = RECEIVE;
end
end
endcase
end
//-----------------------------------------------------------------
// PS2 RX/TX Logic
//-----------------------------------------------------------------
always @(posedge sys_clk) begin
if(sys_rst) begin
rx_clk_data <= 1'd1;
rx_clk_count <= 5'd0;
rx_bitcount <= 5'd0;
rx_data <= 11'b11111111111;
irq <= 1'd0;
csr_do <= 32'd0;
we_reg <= 1'b0;
ps2_data_out2 <= 1'b1;
end else begin
irq <= 1'b0;
we_reg <= 1'b0;
csr_do <= 32'd0;
if(csr_selected) begin
case(csr_a[0])
1'b0: csr_do <= kcode;
1'b1: csr_do <= tx_busy;
endcase
if(csr_we && csr_a[0] == 1'b0) begin
tx_data <= {2'b11, ~(^csr_di[7:0]), csr_di[7:0]}; // STOP+PARITY+DATA
we_reg <= 1'b1;
end
end
if(enable) begin
if(rx_clk_data == ps2_clk_2) begin
rx_clk_count <= rx_clk_count + 5'd1;
end else begin
rx_clk_count <= 5'd0;
rx_clk_data <= ps2_clk_2;
end
if(state_receive && rx_clk_data == 1'b0 && rx_clk_count == 5'd4) begin
rx_data <= {ps2_data_2, rx_data[10:1]};
rx_bitcount <= rx_bitcount + 5'd1;
if(rx_bitcount == 5'd10) begin
irq <= 1'b1;
kcode <= rx_data[9:2];
end
end
if(state_transmit && rx_clk_data == 1'b0 && rx_clk_count == 5'd0) begin
ps2_data_out2 <= tx_data[rx_bitcount];
rx_bitcount <= rx_bitcount + 5'd1;
if(rx_bitcount == 5'd10) begin
ps2_data_out2 <= 1'b1;
end
end
if(rx_clk_count == 5'd16) begin
rx_bitcount <= 5'd0;
rx_data <= 11'b11111111111;
end
end
end
end
assign ps2_clk = ps2_clk_out ? 1'hz : 1'b0;
assign ps2_data = ps2_data_out1 & ps2_data_out2 ? 1'hz : 1'b0;
endmodule

View File

@@ -0,0 +1,23 @@
TEX=uart.tex
DVI=$(TEX:.tex=.dvi)
PS=$(TEX:.tex=.ps)
PDF=$(TEX:.tex=.pdf)
AUX=$(TEX:.tex=.aux)
LOG=$(TEX:.tex=.log)
all: $(PDF)
%.dvi: %.tex
latex $<
%.ps: %.dvi
dvips $<
%.pdf: %.ps
ps2pdf $<
clean:
rm -f $(DVI) $(PS) $(PDF) $(AUX) $(LOG)
.PHONY: clean

View File

@@ -0,0 +1,57 @@
\documentclass[a4paper,11pt]{article}
\usepackage{fullpage}
\usepackage[latin1]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[normalem]{ulem}
\usepackage[english]{babel}
\usepackage{listings,babel}
\lstset{breaklines=true,basicstyle=\ttfamily}
\usepackage{graphicx}
\usepackage{moreverb}
\usepackage{amsmath}
\usepackage{url}
\usepackage{tabularx}
\title{Simple UART}
\author{S\'ebastien Bourdeauducq}
\date{December 2009}
\begin{document}
\setlength{\parindent}{0pt}
\setlength{\parskip}{5pt}
\maketitle{}
\section{Specifications}
The UART is based on a very simple design from Das Labor. Its purpose is basically to provide a debug console.
The UART operates with 8 bits per character, no parity, and 1 stop bit. The default baudrate is configured during synthesis and can be modified at runtime using the divisor register.
The divisor is computed as follows :
\begin{equation*}
\text{divisor} = \frac{\text{Clock frequency (Hz)}}{16 \cdot \text{Bitrate (bps)}}
\end{equation*}
\section{Registers}
\begin{tabularx}{\textwidth}{|l|l|l|X|}
\hline
\bf{Offset} & \bf{Read/Write} & \bf{Default} & \bf{Description} \\
\hline
0x0 & RW & 0x00 & Data register. Received bytes and bytes to transmit are read/written from/to this register. \\
\hline
0x4 & RW & for default bitrate & Divisor register (for bitrate selection). \\
\hline
\end{tabularx}\\
\section{Interrupts}
The core has two active-high edge-sensitive interrupts outputs.
The ``RX'' interrupt is sent whenever a new character is received. The CPU should then read the data register immediately. If a new character is sent before the CPU has had time to read it, the first character will be lost.
The ``TX'' interrupt is sent as soon as the UART finished transmitting a character. When the CPU has written to the data register, it must wait for the interrupt before writing again.
\section{Using the core}
Connect the CSR signals and the interrupts to the system bus and the interrupt controller. The \verb!uart_txd! and \verb!uart_rxd! signals should go to the FPGA pads. You must also provide the desired default baudrate and the system clock frequency in Hz using the parameters.
\section*{Copyright notice}
Copyright \copyright 2007-2009 S\'ebastien Bourdeauducq. \\
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the LICENSE.FDL file at the root of the Milkymist source distribution.
\end{document}

View File

@@ -0,0 +1,87 @@
/*
* Milkymist VJ SoC
* Copyright (C) 2007, 2008, 2009 Sebastien Bourdeauducq
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
module uart #(
parameter csr_addr = 4'h0,
parameter clk_freq = 100000000,
parameter baud = 115200
) (
input sys_clk,
input sys_rst,
input [13:0] csr_a,
input csr_we,
input [31:0] csr_di,
output reg [31:0] csr_do,
output rx_irq,
output tx_irq,
input uart_rxd,
output uart_txd
);
reg [15:0] divisor;
wire [7:0] rx_data;
wire [7:0] tx_data;
wire tx_wr;
uart_transceiver transceiver(
.sys_clk(sys_clk),
.sys_rst(sys_rst),
.uart_rxd(uart_rxd),
.uart_txd(uart_txd),
.divisor(divisor),
.rx_data(rx_data),
.rx_done(rx_irq),
.tx_data(tx_data),
.tx_wr(tx_wr),
.tx_done(tx_irq)
);
/* CSR interface */
wire csr_selected = csr_a[13:10] == csr_addr;
assign tx_data = csr_di[7:0];
assign tx_wr = csr_selected & csr_we & (csr_a[0] == 1'b0);
parameter default_divisor = clk_freq/baud/16;
always @(posedge sys_clk) begin
if(sys_rst) begin
divisor <= default_divisor;
csr_do <= 32'd0;
end else begin
csr_do <= 32'd0;
if(csr_selected) begin
case(csr_a[0])
1'b0: csr_do <= rx_data;
1'b1: csr_do <= divisor;
endcase
if(csr_we) begin
if(csr_a[0] == 1'b1)
divisor <= csr_di[15:0];
end
end
end
end
endmodule

View File

@@ -0,0 +1,157 @@
/*
* Milkymist VJ SoC
* Copyright (C) 2007, 2008, 2009 Sebastien Bourdeauducq
* Copyright (C) 2007 Das Labor
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
module uart_transceiver(
input sys_rst,
input sys_clk,
input uart_rxd,
output reg uart_txd,
input [15:0] divisor,
output reg [7:0] rx_data,
output reg rx_done,
input [7:0] tx_data,
input tx_wr,
output reg tx_done
);
//-----------------------------------------------------------------
// enable16 generator
//-----------------------------------------------------------------
reg [15:0] enable16_counter;
wire enable16;
assign enable16 = (enable16_counter == 16'd0);
always @(posedge sys_clk) begin
if(sys_rst)
enable16_counter <= divisor - 16'b1;
else begin
enable16_counter <= enable16_counter - 16'd1;
if(enable16)
enable16_counter <= divisor - 16'b1;
end
end
//-----------------------------------------------------------------
// Synchronize uart_rxd
//-----------------------------------------------------------------
reg uart_rxd1;
reg uart_rxd2;
always @(posedge sys_clk) begin
uart_rxd1 <= uart_rxd;
uart_rxd2 <= uart_rxd1;
end
//-----------------------------------------------------------------
// UART RX Logic
//-----------------------------------------------------------------
reg rx_busy;
reg [3:0] rx_count16;
reg [3:0] rx_bitcount;
reg [7:0] rxd_reg;
always @(posedge sys_clk) begin
if(sys_rst) begin
rx_done <= 1'b0;
rx_busy <= 1'b0;
rx_count16 <= 4'd0;
rx_bitcount <= 4'd0;
end else begin
rx_done <= 1'b0;
if(enable16) begin
if(~rx_busy) begin // look for start bit
if(~uart_rxd2) begin // start bit found
rx_busy <= 1'b1;
rx_count16 <= 4'd7;
rx_bitcount <= 4'd0;
end
end else begin
rx_count16 <= rx_count16 + 4'd1;
if(rx_count16 == 4'd0) begin // sample
rx_bitcount <= rx_bitcount + 4'd1;
if(rx_bitcount == 4'd0) begin // verify startbit
if(uart_rxd2)
rx_busy <= 1'b0;
end else if(rx_bitcount == 4'd9) begin
rx_busy <= 1'b0;
if(uart_rxd2) begin // stop bit ok
rx_data <= rxd_reg;
rx_done <= 1'b1;
end // ignore RX error
end else
rxd_reg <= {uart_rxd2, rxd_reg[7:1]};
end
end
end
end
end
//-----------------------------------------------------------------
// UART TX Logic
//-----------------------------------------------------------------
reg tx_busy;
reg [3:0] tx_bitcount;
reg [3:0] tx_count16;
reg [7:0] txd_reg;
always @(posedge sys_clk) begin
if(sys_rst) begin
tx_done <= 1'b0;
tx_busy <= 1'b0;
uart_txd <= 1'b1;
end else begin
tx_done <= 1'b0;
if(tx_wr) begin
txd_reg <= tx_data;
tx_bitcount <= 4'd0;
tx_count16 <= 4'd1;
tx_busy <= 1'b1;
uart_txd <= 1'b0;
`ifdef SIMULATION
$display("UART: %c", tx_data);
`endif
end else if(enable16 && tx_busy) begin
tx_count16 <= tx_count16 + 4'd1;
if(tx_count16 == 4'd0) begin
tx_bitcount <= tx_bitcount + 4'd1;
if(tx_bitcount == 4'd8) begin
uart_txd <= 1'b1;
end else if(tx_bitcount == 4'd9) begin
uart_txd <= 1'b1;
tx_busy <= 1'b0;
tx_done <= 1'b1;
end else begin
uart_txd <= txd_reg[0];
txd_reg <= {1'b0, txd_reg[7:1]};
end
end
end
end
end
endmodule

View File

@@ -0,0 +1,23 @@
TEX=vgafb.tex
DVI=$(TEX:.tex=.dvi)
PS=$(TEX:.tex=.ps)
PDF=$(TEX:.tex=.pdf)
AUX=$(TEX:.tex=.aux)
LOG=$(TEX:.tex=.log)
all: $(PDF)
%.dvi: %.tex
latex $<
%.ps: %.dvi
dvips $<
%.pdf: %.ps
ps2pdf $<
clean:
rm -f $(DVI) $(PS) $(PDF) $(AUX) $(LOG)
.PHONY: clean

View File

@@ -0,0 +1,74 @@
\documentclass[a4paper,11pt]{article}
\usepackage{fullpage}
\usepackage[latin1]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[normalem]{ulem}
\usepackage[english]{babel}
\usepackage{listings,babel}
\lstset{breaklines=true,basicstyle=\ttfamily}
\usepackage{graphicx}
\usepackage{moreverb}
\usepackage{url}
\usepackage{amsmath}
\title{VGA framebuffer}
\author{S\'ebastien Bourdeauducq}
\date{December 2009}
\begin{document}
\setlength{\parindent}{0pt}
\setlength{\parskip}{5pt}
\maketitle{}
\section{Specifications}
The VGA framebuffer core enables a system-on-chip to support a VGA video output with the picture read from a memory framebuffer.
The core directly drives a 3-channel 8-bit digital to analog converter and the horizontal and vertical synchronization signals.
The framebuffer is read with a 4x64 FastMemoryLink (FML) master; and a CSR interface is implemented for configuring the video output.
\section{Registers}
\subsection{Control register, offset 0x00}
This register enables or disables the video output by setting or clearing the reset bit 0. At reset, the default value is 0x1.
\subsection{Horizontal video parameters, offsets 0x04, 0x08, 0x0c and 0x10}
Those registers set respectively:
\begin{itemize}
\item the horizontal size of the active video area (the horizontal resolution)
\item the position of the beginning of the horizontal sync pulse in the scan line, in pixel clocks
\item the position of the end of the horizontal sync pulse in the scan line, in pixel clocks
\item the total length of the horizontal scan line minus one, in pixels
\end{itemize}
The default values are for the standard VGA resolution of 640x480 at 60Hz with a 25MHz pixel clock.
\subsection{Vertical video parameters, offsets 0x14, 0x18, 0x1c and 0x20}
Those registers set respectively:
\begin{itemize}
\item the vertical size of the active video area (the vertical resolution)
\item the position of the beginning of the vertical sync pulse. The unit is the horizontal scan line.
\item the position of the end of the vertical sync pulse. Same unit as above.
\item the total count of horizontal scan lines minus one. Same unit as above.
\end{itemize}
The default values are for the standard VGA resolution of 640x480 at 60Hz with a 25MHz pixel clock.
\subsection{DMA control registers, offsets 0x24, 0x28 and 0x2c}
The register 0x24 defines the base address of the framebuffer. That framebuffer is basic progressive scan buffer using the RGB565 pixel format.
When register 0x24 is written, the framebuffer address is not updated immediately. Instead, the VGA core waits for the end of the vertical active video area and only starts fetching data from the new framebuffer at the beginning of the next frame. This enables the use of multiple framebuffers without any tearing or flickering artifacts. The address from which the core is currently reading data is available in register 0x28.
When registers 0x24 and 0x28 have different values, a framebuffer address change is pending. When they have the same values, the frame being displayed is the latest that was asked for.
The framebuffer must be aligned to the start of a FML burst ($\frac{4 \cdot 64}{8}$ bytes).
Register 0x2c defines the number of FML bursts required to fill a complete screen. This is typically set to:
\[
\frac{\text{horizontal resolution} \cdot \text{vertical resolution} \cdot 16}{4 \cdot 64}
\]
The screen resolution must be set so that this number is integer. This is the case with common VGA resolutions.
\section{Connections}
The pixel clock is not generated internally and must be fed to the core using the \verb!vga_clk! port. No relationship is expected with the system clock (the two domains are entirely independent). That pixel clock should also be fed to the synchronous DAC.
The other ports should be self-explanatory.
\section*{Copyright notice}
Copyright \copyright 2007-2009 S\'ebastien Bourdeauducq. \\
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the LICENSE.FDL file at the root of the Milkymist source distribution.
\end{document}

View File

@@ -0,0 +1,229 @@
/*
* Milkymist VJ SoC
* Copyright (C) 2007, 2008, 2009, 2010 Sebastien Bourdeauducq
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
module vgafb #(
parameter csr_addr = 4'h0,
parameter fml_depth = 26
) (
input sys_clk,
input sys_rst,
/* Configuration interface */
input [13:0] csr_a,
input csr_we,
input [31:0] csr_di,
output [31:0] csr_do,
/* Framebuffer FML 4x64 interface */
output [fml_depth-1:0] fml_adr,
output fml_stb,
input fml_ack,
input [63:0] fml_di,
/* VGA pixel clock */
input vga_clk,
/* VGA signal pads */
output vga_psave_n,
output reg vga_hsync_n,
output reg vga_vsync_n,
output vga_sync_n,
output vga_blank_n,
output reg [7:0] vga_r,
output reg [7:0] vga_g,
output reg [7:0] vga_b,
output [1:0] vga_clk_sel
);
/*
* Control interface
*/
wire vga_rst;
wire [10:0] hres;
wire [10:0] hsync_start;
wire [10:0] hsync_end;
wire [10:0] hscan;
wire [10:0] vres;
wire [10:0] vsync_start;
wire [10:0] vsync_end;
wire [10:0] vscan;
wire [fml_depth-1:0] baseaddress;
wire baseaddress_ack;
wire [17:0] nbursts;
vgafb_ctlif #(
.csr_addr(csr_addr),
.fml_depth(fml_depth)
) ctlif (
.sys_clk(sys_clk),
.sys_rst(sys_rst),
.csr_a(csr_a),
.csr_we(csr_we),
.csr_di(csr_di),
.csr_do(csr_do),
.vga_rst(vga_rst),
.hres(hres),
.hsync_start(hsync_start),
.hsync_end(hsync_end),
.hscan(hscan),
.vres(vres),
.vsync_start(vsync_start),
.vsync_end(vsync_end),
.vscan(vscan),
.baseaddress(baseaddress),
.baseaddress_ack(baseaddress_ack),
.nbursts(nbursts),
.vga_clk_sel(vga_clk_sel)
);
/*
* Generate signal data
*/
reg hsync_n;
reg vsync_n;
wire pixel_valid;
wire [15:0] pixel_fb;
wire pixel_ack;
wire [15:0] pixel;
wire fifo_full;
reg hactive;
reg vactive;
wire active = hactive & vactive;
assign pixel = active ? pixel_fb : 16'h0000;
wire generate_en;
reg [10:0] hcounter;
reg [10:0] vcounter;
always @(posedge sys_clk) begin
if(vga_rst) begin
hcounter <= 10'd0;
vcounter <= 10'd0;
hactive <= 1'b0;
hsync_n <= 1'b1;
vactive <= 1'b0;
vsync_n <= 1'b1;
end else begin
if(generate_en) begin
hcounter <= hcounter + 10'd1;
if(hcounter == 10'd0) hactive <= 1'b1;
if(hcounter == hres) hactive <= 1'b0;
if(hcounter == hsync_start) hsync_n <= 1'b0;
if(hcounter == hsync_end) hsync_n <= 1'b1;
if(hcounter == hscan) begin
hcounter <= 10'd0;
if(vcounter == vscan)
vcounter <= 10'd0;
else
vcounter <= vcounter + 10'd1;
end
if(vcounter == 10'd0) vactive <= 1'b1;
if(vcounter == vres) vactive <= 1'b0;
if(vcounter == vsync_start) vsync_n <= 1'b0;
if(vcounter == vsync_end) vsync_n <= 1'b1;
end
end
end
assign generate_en = ~fifo_full & (~active | pixel_valid);
assign pixel_ack = ~fifo_full & active & pixel_valid;
vgafb_pixelfeed #(
.fml_depth(fml_depth)
) pixelfeed (
.sys_clk(sys_clk),
.sys_rst(sys_rst),
.vga_rst(vga_rst),
.nbursts(nbursts),
.baseaddress(baseaddress),
.baseaddress_ack(baseaddress_ack),
.fml_adr(fml_adr),
.fml_stb(fml_stb),
.fml_ack(fml_ack),
.fml_di(fml_di),
.pixel_valid(pixel_valid),
.pixel(pixel_fb),
.pixel_ack(pixel_ack)
);
/*
* System clock to VGA clock domain crossing is
* acheived by an asynchronous FIFO.
*
* Bits 0-15 are RGB565 pixel data
* Bit 16 is negated Horizontal Sync
* Bit 17 is negated Verical Sync
*/
wire [17:0] fifo_do;
vgafb_asfifo #(
.DATA_WIDTH(18),
.ADDRESS_WIDTH(6)
) fifo (
.Data_out(fifo_do),
.Empty_out(),
.ReadEn_in(1'b1),
.RClk(vga_clk),
.Data_in({vsync_n, hsync_n, pixel}),
.Full_out(fifo_full),
.WriteEn_in(generate_en),
.WClk(sys_clk),
.Clear_in(vga_rst)
);
/*
* Drive the VGA pads.
* RGB565 -> RGB888 color space conversion is also performed here
* by bit shifting and replicating the most significant bits of
* the input into the least significant bits of the output left
* undefined by the shifting.
*/
assign vga_sync_n = 1'b0; /* Sync-on-Green is not implemented */
assign vga_psave_n = 1'b1;
assign vga_blank_n = 1'b1;
always @(posedge vga_clk) begin
vga_vsync_n <= fifo_do[17];
vga_hsync_n <= fifo_do[16];
vga_r <= {fifo_do[15:11], fifo_do[15:13]};
vga_g <= {fifo_do[10:5], fifo_do[10:9]};
vga_b <= {fifo_do[4:0], fifo_do[4:2]};
end
endmodule

View File

@@ -0,0 +1,116 @@
//==========================================
// Function : Asynchronous FIFO (w/ 2 asynchronous clocks).
// Coder : Alex Claros F.
// Date : 15/May/2005.
// Notes : This implementation is based on the article
// 'Asynchronous FIFO in Virtex-II FPGAs'
// writen by Peter Alfke. This TechXclusive
// article can be downloaded from the
// Xilinx website. It has some minor modifications.
//=========================================
`timescale 1ns / 1ps
module vgafb_asfifo
#(parameter DATA_WIDTH = 8,
ADDRESS_WIDTH = 4,
FIFO_DEPTH = (1 << ADDRESS_WIDTH))
//Reading port
(output wire [DATA_WIDTH-1:0] Data_out,
output reg Empty_out,
input wire ReadEn_in,
input wire RClk,
//Writing port.
input wire [DATA_WIDTH-1:0] Data_in,
output reg Full_out,
input wire WriteEn_in,
input wire WClk,
input wire Clear_in);
/////Internal connections & variables//////
reg [DATA_WIDTH-1:0] Mem [FIFO_DEPTH-1:0];
wire [ADDRESS_WIDTH-1:0] pNextWordToWrite, pNextWordToRead;
wire EqualAddresses;
wire NextWriteAddressEn, NextReadAddressEn;
wire Set_Status, Rst_Status;
reg Status;
wire PresetFull, PresetEmpty;
//////////////Code///////////////
//Data ports logic:
//(Uses a dual-port RAM).
//'Data_out' logic:
assign Data_out = Mem[pNextWordToRead];
// always @ (posedge RClk)
// if (!PresetEmpty)
// Data_out <= Mem[pNextWordToRead];
// if (ReadEn_in & !Empty_out)
//'Data_in' logic:
always @ (posedge WClk)
if (WriteEn_in & !Full_out)
Mem[pNextWordToWrite] <= Data_in;
//Fifo addresses support logic:
//'Next Addresses' enable logic:
assign NextWriteAddressEn = WriteEn_in & ~Full_out;
assign NextReadAddressEn = ReadEn_in & ~Empty_out;
//Addreses (Gray counters) logic:
vgafb_graycounter #(
.COUNTER_WIDTH( ADDRESS_WIDTH )
) GrayCounter_pWr (
.GrayCount_out(pNextWordToWrite),
.Enable_in(NextWriteAddressEn),
.Clear_in(Clear_in),
.Clk(WClk)
);
vgafb_graycounter #(
.COUNTER_WIDTH( ADDRESS_WIDTH )
) GrayCounter_pRd (
.GrayCount_out(pNextWordToRead),
.Enable_in(NextReadAddressEn),
.Clear_in(Clear_in),
.Clk(RClk)
);
//'EqualAddresses' logic:
assign EqualAddresses = (pNextWordToWrite == pNextWordToRead);
//'Quadrant selectors' logic:
assign Set_Status = (pNextWordToWrite[ADDRESS_WIDTH-2] ~^ pNextWordToRead[ADDRESS_WIDTH-1]) &
(pNextWordToWrite[ADDRESS_WIDTH-1] ^ pNextWordToRead[ADDRESS_WIDTH-2]);
assign Rst_Status = (pNextWordToWrite[ADDRESS_WIDTH-2] ^ pNextWordToRead[ADDRESS_WIDTH-1]) &
(pNextWordToWrite[ADDRESS_WIDTH-1] ~^ pNextWordToRead[ADDRESS_WIDTH-2]);
//'Status' latch logic:
always @ (Set_Status, Rst_Status, Clear_in) //D Latch w/ Asynchronous Clear & Preset.
if (Rst_Status | Clear_in)
Status = 0; //Going 'Empty'.
else if (Set_Status)
Status = 1; //Going 'Full'.
//'Full_out' logic for the writing port:
assign PresetFull = Status & EqualAddresses; //'Full' Fifo.
always @ (posedge WClk, posedge PresetFull) //D Flip-Flop w/ Asynchronous Preset.
if (PresetFull)
Full_out <= 1;
else
Full_out <= 0;
//'Empty_out' logic for the reading port:
assign PresetEmpty = ~Status & EqualAddresses; //'Empty' Fifo.
always @ (posedge RClk, posedge PresetEmpty) //D Flip-Flop w/ Asynchronous Preset.
if (PresetEmpty)
Empty_out <= 1;
else
Empty_out <= 0;
endmodule

View File

@@ -0,0 +1,93 @@
/*
* Milkymist VJ SoC
* Copyright (C) 2007, 2008, 2009, 2010 Sebastien Bourdeauducq
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* FIXME: this module does not work. Find out why. */
module vgafb_asfifo #(
/* NB: those are fixed in this implementation */
parameter DATA_WIDTH = 18,
parameter ADDRESS_WIDTH = 11
) (
/* Reading port */
output [17:0] Data_out,
output Empty_out,
input ReadEn_in,
input RClk,
/* Writing port */
input [17:0] Data_in,
output Full_out,
input WriteEn_in,
input WClk,
input Clear_in
);
wire full;
wire empty;
FIFO16 #(
.DATA_WIDTH(9),
.FIRST_WORD_FALL_THROUGH("TRUE")
) fifo_lo (
.ALMOSTEMPTY(),
.ALMOSTFULL(),
.DO(Data_out[7:0]),
.DOP(Data_out[8]),
.EMPTY(empty),
.FULL(full),
.RDCOUNT(),
.RDERR(),
.WRCOUNT(),
.WRERR(),
.DI(Data_in[7:0]),
.DIP(Data_in[8]),
.RDCLK(RClk),
.RDEN(ReadEn_in & ~empty & ~Clear_in),
.RST(Clear_in),
.WRCLK(WClk),
.WREN(WriteEn_in & ~full & ~Clear_in)
);
assign Empty_out = empty;
assign Full_out = full;
FIFO16 #(
.DATA_WIDTH(9),
.FIRST_WORD_FALL_THROUGH("TRUE")
) fifo_hi (
.ALMOSTEMPTY(),
.ALMOSTFULL(),
.DO(Data_out[16:9]),
.DOP(Data_out[17]),
.EMPTY(),
.FULL(),
.RDCOUNT(),
.RDERR(),
.WRCOUNT(),
.WRERR(),
.DI(Data_in[16:9]),
.DIP(Data_in[17]),
.RDCLK(RClk),
.RDEN(ReadEn_in & ~empty & ~Clear_in),
.RST(Clear_in),
.WRCLK(WClk),
.WREN(WriteEn_in & ~full & ~Clear_in)
);
endmodule

View File

@@ -0,0 +1,121 @@
/*
* Milkymist VJ SoC
* Copyright (C) 2007, 2008, 2009 Sebastien Bourdeauducq
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
module vgafb_ctlif #(
parameter csr_addr = 4'h0,
parameter fml_depth = 26
) (
input sys_clk,
input sys_rst,
input [13:0] csr_a,
input csr_we,
input [31:0] csr_di,
output reg [31:0] csr_do,
output reg vga_rst,
output reg [10:0] hres,
output reg [10:0] hsync_start,
output reg [10:0] hsync_end,
output reg [10:0] hscan,
output reg [10:0] vres,
output reg [10:0] vsync_start,
output reg [10:0] vsync_end,
output reg [10:0] vscan,
output reg [fml_depth-1:0] baseaddress,
input baseaddress_ack,
output reg [17:0] nbursts,
output reg [1:0] vga_clk_sel
);
reg [fml_depth-1:0] baseaddress_act;
always @(posedge sys_clk) begin
if(sys_rst)
baseaddress_act <= {fml_depth{1'b0}};
else if(baseaddress_ack)
baseaddress_act <= baseaddress;
end
wire csr_selected = csr_a[13:10] == csr_addr;
always @(posedge sys_clk) begin
if(sys_rst) begin
csr_do <= 32'd0;
vga_rst <= 1'b1;
hres <= 10'd640;
hsync_start <= 10'd656;
hsync_end <= 10'd752;
hscan <= 10'd799;
vres <= 10'd480;
vsync_start <= 10'd491;
vsync_end <= 10'd493;
vscan <= 10'd523;
baseaddress <= {fml_depth{1'b0}};
nbursts <= 18'd19200;
vga_clk_sel <= 2'd00;
end else begin
csr_do <= 32'd0;
if(csr_selected) begin
if(csr_we) begin
case(csr_a[3:0])
4'd0: vga_rst <= csr_di[0];
4'd1: hres <= csr_di[10:0];
4'd2: hsync_start <= csr_di[10:0];
4'd3: hsync_end <= csr_di[10:0];
4'd4: hscan <= csr_di[10:0];
4'd5: vres <= csr_di[10:0];
4'd6: vsync_start <= csr_di[10:0];
4'd7: vsync_end <= csr_di[10:0];
4'd8: vscan <= csr_di[10:0];
4'd9: baseaddress <= csr_di[fml_depth-1:0];
// 10: baseaddress_act is read-only for Wishbone
4'd11: nbursts <= csr_di[17:0];
4'd12: vga_clk_sel <= csr_di[1:0];
endcase
end
case(csr_a[3:0])
4'd0: csr_do <= vga_rst;
4'd1: csr_do <= hres;
4'd2: csr_do <= hsync_start;
4'd3: csr_do <= hsync_end;
4'd4: csr_do <= hscan;
4'd5: csr_do <= vres;
4'd6: csr_do <= vsync_start;
4'd7: csr_do <= vsync_end;
4'd8: csr_do <= vscan;
4'd9: csr_do <= baseaddress;
4'd10: csr_do <= baseaddress_act;
4'd11: csr_do <= nbursts;
4'd12: csr_do <= vga_clk_sel;
endcase
end
end
end
endmodule

View File

@@ -0,0 +1,75 @@
/*
* Milkymist VJ SoC
* Copyright (C) 2007, 2008, 2009 Sebastien Bourdeauducq
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
module vgafb_fifo64to16(
input sys_clk,
input vga_rst,
input stb,
input [63:0] di,
output do_valid,
output reg [15:0] do,
input next /* should only be asserted when do_valid = 1 */
);
/*
* FIFO can hold 4 64-bit words
* that is 16 16-bit words.
*/
reg [63:0] storage[0:3];
reg [1:0] produce; /* in 64-bit words */
reg [3:0] consume; /* in 16-bit words */
/*
* 16-bit words stored in the FIFO, 0-16 (17 possible values)
*/
reg [4:0] level;
wire [63:0] do64;
assign do64 = storage[consume[3:2]];
always @(*) begin
case(consume[1:0])
2'd0: do <= do64[63:48];
2'd1: do <= do64[47:32];
2'd2: do <= do64[31:16];
2'd3: do <= do64[15:0];
endcase
end
always @(posedge sys_clk) begin
if(vga_rst) begin
produce = 2'd0;
consume = 4'd0;
level = 5'd0;
end else begin
if(stb) begin
storage[produce] = di;
produce = produce + 2'd1;
level = level + 5'd4;
end
if(next) begin /* next should only be asserted when do_valid = 1 */
consume = consume + 4'd1;
level = level - 5'd1;
end
end
end
assign do_valid = ~(level == 5'd0);
endmodule

View File

@@ -0,0 +1,35 @@
//==========================================
// Function : Code Gray counter.
// Coder : Alex Claros F.
// Date : 15/May/2005.
//=======================================
`timescale 1ns/1ps
module vgafb_graycounter
#(parameter COUNTER_WIDTH = 2)
(output reg [COUNTER_WIDTH-1:0] GrayCount_out, //'Gray' code count output.
input wire Enable_in, //Count enable.
input wire Clear_in, //Count reset.
input wire Clk);
/////////Internal connections & variables///////
reg [COUNTER_WIDTH-1:0] BinaryCount;
/////////Code///////////////////////
always @ (posedge Clk)
if (Clear_in) begin
BinaryCount <= {COUNTER_WIDTH{1'b 0}} + 1; //Gray count begins @ '1' with
GrayCount_out <= {COUNTER_WIDTH{1'b 0}}; // first 'Enable_in'.
end
else if (Enable_in) begin
BinaryCount <= BinaryCount + 1;
GrayCount_out <= {BinaryCount[COUNTER_WIDTH-1],
BinaryCount[COUNTER_WIDTH-2:0] ^ BinaryCount[COUNTER_WIDTH-1:1]};
end
endmodule

View File

@@ -0,0 +1,182 @@
/*
* Milkymist VJ SoC
* Copyright (C) 2007, 2008, 2009 Sebastien Bourdeauducq
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
module vgafb_pixelfeed #(
parameter fml_depth = 26
) (
input sys_clk,
/* We must take into account both resets :
* VGA reset should not interrupt a pending FML request
* but system reset should.
*/
input sys_rst,
input vga_rst,
input [17:0] nbursts,
input [fml_depth-1:0] baseaddress,
output baseaddress_ack,
output reg [fml_depth-1:0] fml_adr,
output reg fml_stb,
input fml_ack,
input [63:0] fml_di,
output pixel_valid,
output [15:0] pixel,
input pixel_ack
);
/* FIFO that stores the 64-bit bursts and slices it in 16-bit words */
reg fifo_stb;
wire fifo_valid;
vgafb_fifo64to16 fifo64to16(
.sys_clk(sys_clk),
.vga_rst(vga_rst),
.stb(fifo_stb),
.di(fml_di),
.do_valid(fifo_valid),
.do(pixel),
.next(pixel_ack)
);
assign pixel_valid = fifo_valid;
/* BURST COUNTER */
reg sof;
wire counter_en;
reg [17:0] bcounter;
always @(posedge sys_clk) begin
if(vga_rst) begin
bcounter <= 18'd1;
sof <= 1'b1;
end else begin
if(counter_en) begin
if(bcounter == nbursts) begin
bcounter <= 18'd1;
sof <= 1'b1;
end else begin
bcounter <= bcounter + 18'd1;
sof <= 1'b0;
end
end
end
end
/* FML ADDRESS GENERATOR */
wire next_address;
assign baseaddress_ack = sof & next_address;
always @(posedge sys_clk) begin
if(sys_rst) begin
fml_adr <= {fml_depth{1'b0}};
end else begin
if(next_address) begin
if(sof)
fml_adr <= baseaddress;
else
fml_adr <= fml_adr + {{fml_depth-6{1'b0}}, 6'd32};
end
end
end
/* CONTROLLER */
reg [2:0] state;
reg [2:0] next_state;
parameter IDLE = 3'd0;
parameter WAIT = 3'd1;
parameter FETCH2 = 3'd2;
parameter FETCH3 = 3'd3;
parameter FETCH4 = 3'd4;
always @(posedge sys_clk) begin
if(sys_rst)
state <= IDLE;
else
state <= next_state;
end
/*
* Do not put spurious data into the FIFO if the VGA reset
* is asserted and released during the FML access. Getting
* the FIFO out of sync would result in distorted pictures
* we really want to avoid.
*/
reg ignore;
reg ignore_clear;
always @(posedge sys_clk) begin
if(vga_rst)
ignore <= 1'b1;
else if(ignore_clear)
ignore <= 1'b0;
end
reg next_burst;
assign counter_en = next_burst;
assign next_address = next_burst;
always @(*) begin
next_state = state;
fifo_stb = 1'b0;
next_burst = 1'b0;
fml_stb = 1'b0;
ignore_clear = 1'b0;
case(state)
IDLE: begin
if(~fifo_valid & ~vga_rst) begin
/* We're in need of pixels ! */
next_burst = 1'b1;
ignore_clear = 1'b1;
next_state = WAIT;
end
end
WAIT: begin
fml_stb = 1'b1;
if(fml_ack) begin
if(~ignore) fifo_stb = 1'b1;
next_state = FETCH2;
end
end
FETCH2: begin
if(~ignore) fifo_stb = 1'b1;
next_state = FETCH3;
end
FETCH3: begin
if(~ignore) fifo_stb = 1'b1;
next_state = FETCH4;
end
FETCH4: begin
if(~ignore) fifo_stb = 1'b1;
next_state = IDLE;
end
endcase
end
endmodule

View File

@@ -0,0 +1,11 @@
SOURCES_PIXELFEED=tb_pixelfeed.v ../rtl/vgafb_pixelfeed.v ../rtl/vgafb_fifo64to16.v
all: pixelfeed
pixelfeed: $(SOURCES_PIXELFEED)
cver $(SOURCES_PIXELFEED)
clean:
rm -f verilog.log
.PHONY: clean pixelfeed

View File

@@ -0,0 +1,61 @@
/*
* Milkymist VJ SoC
* Copyright (C) 2007, 2008, 2009 Sebastien Bourdeauducq
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
module tb_pixelfeed();
reg sys_clk;
initial sys_clk = 1'b0;
always #5 sys_clk = ~sys_clk;
reg sys_rst;
reg vga_rst;
wire pixel_valid;
wire fml_stb;
wire [25:0] fml_adr;
initial begin
sys_rst = 1'b1;
vga_rst = 1'b1;
#20 sys_rst = 1'b0;
#20 vga_rst = 1'b0;
end
vgafb_pixelfeed dut(
.sys_clk(sys_clk),
.sys_rst(sys_rst),
.vga_rst(vga_rst),
.nbursts(18'd100),
.baseaddress(26'd1024),
.baseaddress_ack(),
.fml_adr(fml_adr),
.fml_stb(fml_stb),
.fml_ack(fml_stb),
.fml_di(64'hcafebabedeadbeef),
.pixel_valid(pixel_valid),
.pixel(),
.pixel_ack(pixel_valid)
);
always @(posedge sys_clk) $display("%x", fml_adr);
initial #600 $finish;
endmodule

View File

@@ -51,8 +51,8 @@ initial begin
$dumpvars(-1, dut);
// reset
#0 rst <= 1;
#80 rst <= 0;
#0 rst <= 0;
#80 rst <= 1;
#(tck*10000) $finish;
end