generated from saji/ecp5-template
Initial commit
This commit is contained in:
commit
03370cff34
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
*.bit
|
||||
*.o
|
||||
*.bin
|
||||
build/*
|
4
Makefile
Normal file
4
Makefile
Normal file
|
@ -0,0 +1,4 @@
|
|||
|
||||
NEXTPNR_FLAGS= --45k --package CABGA381 --speed 6 --freq 65
|
||||
YOSYS_FLAGS=
|
||||
|
11
README.md
Normal file
11
README.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
# ECP5 OSS Flow Template
|
||||
|
||||
This repo contains a basic setup for ECP5 development.
|
||||
It's designed to serve as a modest baseline, more can be added easily
|
||||
to adapt to each project.
|
||||
|
||||
Features:
|
||||
- Nix flake for toolchains and dependencies
|
||||
- Test infra
|
||||
- Github Actions Workflow
|
||||
|
27
flake.lock
Normal file
27
flake.lock
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1712791164,
|
||||
"narHash": "sha256-3sbWO1mbpWsLepZGbWaMovSO7ndZeFqDSdX0hZ9nVyw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "1042fd8b148a9105f3c0aca3a6177fd1d9360ba5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
60
flake.nix
Normal file
60
flake.nix
Normal file
|
@ -0,0 +1,60 @@
|
|||
{
|
||||
description = "ECP5 toolchain template project";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
};
|
||||
|
||||
outputs = inputs@{ nixpkgs, ... }:
|
||||
let
|
||||
# litex-overlay = final: prev: {
|
||||
# pythonPackagesExtensions = prev.pythonPackagesExtensions ++ [
|
||||
# (python-final: python-prev: {
|
||||
# litex = python-final.callPackage (import ./litex.nix) { };
|
||||
# # can add more packages here!
|
||||
# })
|
||||
# ];
|
||||
# };
|
||||
litex-overlay = import ./litex;
|
||||
systems = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin" ];
|
||||
forAllSystems = function:
|
||||
nixpkgs.lib.genAttrs systems (system: function (
|
||||
import nixpkgs {
|
||||
inherit system;
|
||||
config.allowUnfree = true;
|
||||
overlays = [
|
||||
litex-overlay
|
||||
]; # patches, version pins, new pkgs here.
|
||||
}
|
||||
));
|
||||
in {
|
||||
devShells = forAllSystems (pkgs: {
|
||||
default = pkgs.mkShell {
|
||||
packages = with pkgs; [
|
||||
(python3.withPackages (ps: with ps; [
|
||||
cocotb
|
||||
cocotb-bus
|
||||
litex
|
||||
litedram
|
||||
liteeth
|
||||
litescope
|
||||
pythondata-cpu-vexriscv
|
||||
pythondata-software-compiler_rt
|
||||
pythondata-software-picolibc
|
||||
amaranth
|
||||
]))
|
||||
yosys
|
||||
nextpnr
|
||||
# simulators
|
||||
verilog
|
||||
verilator
|
||||
# support package
|
||||
trellis
|
||||
# loader
|
||||
openfpgaloader
|
||||
ecpdap # easier to poke probes.
|
||||
];
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
17
litex/default.nix
Normal file
17
litex/default.nix
Normal file
|
@ -0,0 +1,17 @@
|
|||
# an overlay to
|
||||
let
|
||||
tag = "2023.12";
|
||||
in final: prev: {
|
||||
pythonPackagesExtensions = prev.pythonPackagesExtensions ++ [
|
||||
(python-final: python-prev: {
|
||||
litex = python-final.callPackage(import ./litex.nix tag) { };
|
||||
litedram = python-final.callPackage(import ./litedram.nix tag) { };
|
||||
liteeth = python-final.callPackage(import ./liteeth.nix tag) { };
|
||||
litescope = python-final.callPackage(import ./litescope.nix tag) { };
|
||||
pythondata-cpu-vexriscv = python-final.callPackage(import ./pythondata-cpu-vexriscv.nix tag) { };
|
||||
pythondata-software-compiler_rt = python-final.callPackage(import ./pythondata-software-compiler_rt.nix tag) { };
|
||||
pythondata-software-picolibc = python-final.callPackage(import ./pythondata-software-picolibc.nix tag) { };
|
||||
})
|
||||
];
|
||||
}
|
||||
|
27
litex/litedram.nix
Normal file
27
litex/litedram.nix
Normal file
|
@ -0,0 +1,27 @@
|
|||
tag: {
|
||||
pkgs
|
||||
, lib
|
||||
, buildPythonPackage
|
||||
, migen
|
||||
, pyyaml
|
||||
}: buildPythonPackage {
|
||||
pname = "litedram";
|
||||
version = "${tag}";
|
||||
|
||||
src = pkgs.fetchFromGitHub {
|
||||
owner = "enjoy-digital";
|
||||
repo = "litedram";
|
||||
rev = "${tag}";
|
||||
hash = "sha256-EaUszy0v6r5sKM5d5YmpRbR8Cf9xITObU71zUpg9cLU=";
|
||||
};
|
||||
|
||||
buildInputs = [
|
||||
pyyaml
|
||||
migen
|
||||
];
|
||||
|
||||
checkPhase = ''
|
||||
python -m unittest test
|
||||
'';
|
||||
doCheck = true;
|
||||
}
|
36
litex/liteeth.nix
Normal file
36
litex/liteeth.nix
Normal file
|
@ -0,0 +1,36 @@
|
|||
tag: {
|
||||
pkgs
|
||||
, lib
|
||||
, buildPythonPackage
|
||||
, migen
|
||||
, setuptools
|
||||
, litex
|
||||
, pyyaml
|
||||
}: buildPythonPackage {
|
||||
pname = "liteeth";
|
||||
version = "${tag}";
|
||||
|
||||
src = pkgs.fetchFromGitHub {
|
||||
owner = "enjoy-digital";
|
||||
repo = "liteeth";
|
||||
rev = "${tag}";
|
||||
hash = "sha256-DUNwDzcFLVmL5F/ZWmok7T7jO7ixC9IuDr1WUarnAqk=";
|
||||
};
|
||||
|
||||
buildInputs = [
|
||||
litex
|
||||
];
|
||||
|
||||
propagatedBuildInputs = [
|
||||
migen
|
||||
];
|
||||
|
||||
nativeCheckInputs = [
|
||||
pyyaml
|
||||
];
|
||||
|
||||
checkPhase = ''
|
||||
python -m unittest test
|
||||
'';
|
||||
doCheck = true;
|
||||
}
|
27
litex/litescope.nix
Normal file
27
litex/litescope.nix
Normal file
|
@ -0,0 +1,27 @@
|
|||
tag: {
|
||||
pkgs
|
||||
, lib
|
||||
, buildPythonPackage
|
||||
, migen
|
||||
, setuptools
|
||||
, litex
|
||||
}: buildPythonPackage {
|
||||
pname = "litescope";
|
||||
version = "${tag}";
|
||||
|
||||
src = pkgs.fetchFromGitHub {
|
||||
owner = "enjoy-digital";
|
||||
repo = "litescope";
|
||||
rev = "${tag}";
|
||||
hash = "sha256-OWC+XwB+BzlCQkPKJCbQ0W4T6JbsInldrn9jYYjWypM=";
|
||||
};
|
||||
|
||||
buildInputs = [
|
||||
litex
|
||||
];
|
||||
|
||||
checkPhase = ''
|
||||
python -m unittest test
|
||||
'';
|
||||
doCheck = true;
|
||||
}
|
32
litex/litex.nix
Normal file
32
litex/litex.nix
Normal file
|
@ -0,0 +1,32 @@
|
|||
tag: {
|
||||
pkgs
|
||||
, lib
|
||||
, buildPythonPackage
|
||||
, pyserial
|
||||
, migen
|
||||
, requests
|
||||
, packaging
|
||||
, pexpect
|
||||
}: buildPythonPackage {
|
||||
pname = "litex";
|
||||
version = "${tag}";
|
||||
|
||||
src = pkgs.fetchFromGitHub {
|
||||
owner = "enjoy-digital";
|
||||
repo = "litex";
|
||||
rev = "${tag}";
|
||||
hash = "sha256-OcwqYLQ7ec2vTewdIJqP/aTCJ4yI43OIOkTMD/hIKO0=";
|
||||
};
|
||||
|
||||
buildInputs = [
|
||||
migen
|
||||
];
|
||||
|
||||
propagatedBuildInputs = [
|
||||
requests
|
||||
pyserial
|
||||
packaging
|
||||
];
|
||||
|
||||
doCheck = false;
|
||||
}
|
16
litex/pythondata-cpu-vexriscv.nix
Normal file
16
litex/pythondata-cpu-vexriscv.nix
Normal file
|
@ -0,0 +1,16 @@
|
|||
tag: {
|
||||
pkgs
|
||||
, buildPythonPackage
|
||||
}: buildPythonPackage rec {
|
||||
pname = "pythondata-cpu-vexriscv";
|
||||
version = "${tag}";
|
||||
|
||||
src = pkgs.fetchFromGitHub {
|
||||
owner = "litex-hub";
|
||||
repo = "pythondata-cpu-vexriscv";
|
||||
rev = "${tag}";
|
||||
hash = "sha256-1RgwJCYxtiP5dfRHulfTNBgu6fum6RfoqkbFIlRTgKI=";
|
||||
};
|
||||
|
||||
doCheck = false;
|
||||
}
|
16
litex/pythondata-software-compiler_rt.nix
Normal file
16
litex/pythondata-software-compiler_rt.nix
Normal file
|
@ -0,0 +1,16 @@
|
|||
tag: {
|
||||
pkgs
|
||||
, buildPythonPackage
|
||||
}: buildPythonPackage rec {
|
||||
pname = "pythondata-software-compiler_rt";
|
||||
version = "${tag}";
|
||||
|
||||
src = pkgs.fetchFromGitHub {
|
||||
owner = "litex-hub";
|
||||
repo = "pythondata-software-compiler_rt";
|
||||
rev = "${tag}";
|
||||
hash = "sha256-s/tfxhPwYAnu1z1LxEimeYWjX6IHbF2uD/1HjvQn/xo=";
|
||||
};
|
||||
|
||||
doCheck = false;
|
||||
}
|
16
litex/pythondata-software-picolibc.nix
Normal file
16
litex/pythondata-software-picolibc.nix
Normal file
|
@ -0,0 +1,16 @@
|
|||
tag: {
|
||||
pkgs
|
||||
, buildPythonPackage
|
||||
}: buildPythonPackage rec {
|
||||
pname = "pythondata-software-picolibc";
|
||||
version = "${tag}";
|
||||
|
||||
src = pkgs.fetchFromGitHub {
|
||||
owner = "litex-hub";
|
||||
repo = "pythondata-software-picolibc";
|
||||
rev = "${tag}";
|
||||
hash = "sha256-5OY17BA37c6aHOvUwb0gJwXxGey4TdUiTTxJD5wuSGU=";
|
||||
};
|
||||
|
||||
doCheck = false;
|
||||
}
|
27
src/blink.lpf
Normal file
27
src/blink.lpf
Normal file
|
@ -0,0 +1,27 @@
|
|||
LOCATE COMP "clk_25mhz" SITE "P3";
|
||||
IOBUF PORT "clk_25mhz" IO_TYPE=LVCMOS33;
|
||||
FREQUENCY PORT "clk_25mhz" 25 MHZ;
|
||||
SYSCONFIG COMPRESS_CONFIG=ON;
|
||||
|
||||
LOCATE COMP "led_o" SITE "L2";
|
||||
IOBUF PORT "led_o" IO_TYPE=LVCMOS33;
|
||||
|
||||
|
||||
# HDMI
|
||||
LOCATE COMP "gpdi_dp[0]" SITE "G19"; # Blue +
|
||||
LOCATE COMP "gpdi_dn[0]" SITE "H20"; # Blue -
|
||||
LOCATE COMP "gpdi_dp[1]" SITE "E20"; # Green +
|
||||
LOCATE COMP "gpdi_dn[1]" SITE "F19"; # Green -
|
||||
LOCATE COMP "gpdi_dp[2]" SITE "C20"; # Red +
|
||||
LOCATE COMP "gpdi_dn[2]" SITE "D19"; # Red -
|
||||
LOCATE COMP "gpdi_dp[3]" SITE "J19"; # Clock +
|
||||
LOCATE COMP "gpdi_dn[3]" SITE "K19"; # Clock -
|
||||
|
||||
IOBUF PORT "gpdi_dp[0]" IO_TYPE=LVCMOS33 DRIVE=4;
|
||||
IOBUF PORT "gpdi_dn[0]" IO_TYPE=LVCMOS33 DRIVE=4;
|
||||
IOBUF PORT "gpdi_dp[1]" IO_TYPE=LVCMOS33 DRIVE=4;
|
||||
IOBUF PORT "gpdi_dn[1]" IO_TYPE=LVCMOS33 DRIVE=4;
|
||||
IOBUF PORT "gpdi_dp[2]" IO_TYPE=LVCMOS33 DRIVE=4;
|
||||
IOBUF PORT "gpdi_dn[2]" IO_TYPE=LVCMOS33 DRIVE=4;
|
||||
IOBUF PORT "gpdi_dp[3]" IO_TYPE=LVCMOS33 DRIVE=4;
|
||||
IOBUF PORT "gpdi_dn[3]" IO_TYPE=LVCMOS33 DRIVE=4;
|
570
src/blink.v
Normal file
570
src/blink.v
Normal file
|
@ -0,0 +1,570 @@
|
|||
`default_nettype none
|
||||
|
||||
module blink (
|
||||
input clk_i,
|
||||
output reg led_o
|
||||
);
|
||||
localparam MAX = 25_000_000;
|
||||
localparam WIDTH = $clog2(MAX);
|
||||
|
||||
wire rst_s;
|
||||
wire clk_s;
|
||||
|
||||
assign clk_s = clk_i;
|
||||
//pll_12_16 pll_inst (.clki(clk_i), .clko(clk_s), .rst(rst_s));
|
||||
// rst_gen rst_inst (.clk_i(clk_s), .rst_i(1'b0), .rst_o(rst_s));
|
||||
|
||||
reg [WIDTH-1:0] cpt_s;
|
||||
wire [WIDTH-1:0] cpt_next_s = cpt_s + 1'b1;
|
||||
|
||||
wire end_s = cpt_s == MAX-1;
|
||||
|
||||
initial begin
|
||||
led_o <= 0;
|
||||
end
|
||||
|
||||
always @(posedge clk_s) begin
|
||||
// if we're at the end, reset to 0, else set to next state.
|
||||
cpt_s <= end_s ? {WIDTH{1'b0}} : cpt_next_s;
|
||||
|
||||
if (end_s)
|
||||
led_o <= ~led_o;
|
||||
end
|
||||
endmodule
|
||||
|
||||
|
||||
|
||||
|
||||
module topcore (
|
||||
input clk_25mhz,
|
||||
output [3:0] gpdi_dp, gpdi_dn,
|
||||
);
|
||||
|
||||
|
||||
wire clk_25MHz, clk_250MHz;
|
||||
clock clock_instance(
|
||||
.clkin_25MHz(clk_25mhz),
|
||||
.clk_25MHz(clk_25MHz),
|
||||
.clk_250MHz(clk_250MHz)
|
||||
);
|
||||
|
||||
wire [7:0] red, grn, blu;
|
||||
wire [23:0] pixel;
|
||||
assign red= pixel[23:16];
|
||||
assign grn= pixel[15:8];
|
||||
assign blu= pixel[7:0];
|
||||
|
||||
wire o_red;
|
||||
wire o_grn;
|
||||
wire o_blu;
|
||||
wire o_rd, o_newline, o_newframe;
|
||||
|
||||
// A reset line that goes low after 16 ticks
|
||||
reg [2:0] reset_cnt = 0;
|
||||
wire reset = ~reset_cnt[2];
|
||||
always @(posedge clk_25mhz)
|
||||
if (reset) reset_cnt <= reset_cnt + 1;
|
||||
|
||||
|
||||
llhdmi llhdmi_instance(
|
||||
.i_tmdsclk(clk_250MHz), .i_pixclk(clk_25MHz),
|
||||
.i_reset(reset), .i_red(red), .i_grn(grn), .i_blu(blu),
|
||||
.o_rd(o_rd), .o_newline(o_newline), .o_newframe(o_newframe),
|
||||
.o_red(o_red), .o_grn(o_grn), .o_blu(o_blu));
|
||||
|
||||
vgatestsrc #(.BITS_PER_COLOR(8))
|
||||
vgatestsrc_instance(
|
||||
.i_pixclk(clk_25MHz), .i_reset(reset),
|
||||
.i_width(640), .i_height(480),
|
||||
.i_rd(o_rd), .i_newline(o_newline), .i_newframe(o_newframe),
|
||||
.o_pixel(pixel));
|
||||
|
||||
OBUFDS OBUFDS_red(.I(o_red), .O(gpdi_dp[2]), .OB(gpdi_dn[2]));
|
||||
OBUFDS OBUFDS_grn(.I(o_grn), .O(gpdi_dp[1]), .OB(gpdi_dn[1]));
|
||||
OBUFDS OBUFDS_blu(.I(o_blu), .O(gpdi_dp[0]), .OB(gpdi_dn[0]));
|
||||
OBUFDS OBUFDS_clock(.I(clk_25MHz), .O(gpdi_dp[3]), .OB(gpdi_dn[3]));
|
||||
|
||||
endmodule
|
||||
|
||||
module OBUFDS(
|
||||
input I, // input
|
||||
output O, // positive output
|
||||
output OB // negative output
|
||||
);
|
||||
|
||||
assign O = I;
|
||||
assign OB = ~I;
|
||||
|
||||
endmodule
|
||||
|
||||
module clock
|
||||
(
|
||||
input clkin_25MHz,
|
||||
output clk_125MHz,
|
||||
output clk_250MHz,
|
||||
output clk_25MHz,
|
||||
output clk_83M333Hz,
|
||||
output locked
|
||||
);
|
||||
wire int_locked;
|
||||
|
||||
(* ICP_CURRENT="9" *) (* LPF_RESISTOR="8" *) (* MFG_ENABLE_FILTEROPAMP="1" *) (* MFG_GMCREF_SEL="2" *)
|
||||
EHXPLLL
|
||||
#(
|
||||
.PLLRST_ENA("DISABLED"),
|
||||
.INTFB_WAKE("DISABLED"),
|
||||
.STDBY_ENABLE("DISABLED"),
|
||||
.DPHASE_SOURCE("DISABLED"),
|
||||
.CLKOS_FPHASE(0),
|
||||
.CLKOP_FPHASE(0),
|
||||
.CLKOS3_CPHASE(5),
|
||||
.CLKOS2_CPHASE(0),
|
||||
.CLKOS_CPHASE(1),
|
||||
.CLKOP_CPHASE(3),
|
||||
.OUTDIVIDER_MUXD("DIVD"),
|
||||
.OUTDIVIDER_MUXC("DIVC"),
|
||||
.OUTDIVIDER_MUXB("DIVB"),
|
||||
.OUTDIVIDER_MUXA("DIVA"),
|
||||
.CLKOS3_ENABLE("ENABLED"),
|
||||
.CLKOS2_ENABLE("ENABLED"),
|
||||
.CLKOS_ENABLE("ENABLED"),
|
||||
.CLKOP_ENABLE("ENABLED"),
|
||||
.CLKOS3_DIV(0),
|
||||
.CLKOS2_DIV(20),
|
||||
.CLKOS_DIV(2),
|
||||
.CLKOP_DIV(4),
|
||||
.CLKFB_DIV(5),
|
||||
.CLKI_DIV(1),
|
||||
.FEEDBK_PATH("CLKOP")
|
||||
)
|
||||
pll_i
|
||||
(
|
||||
.CLKI(clkin_25MHz),
|
||||
.CLKFB(clk_125MHz),
|
||||
.CLKOP(clk_125MHz),
|
||||
.CLKOS(clk_250MHz),
|
||||
.CLKOS2(clk_25MHz),
|
||||
.CLKOS3(clk_83M333Hz),
|
||||
.RST(1'b0),
|
||||
.STDBY(1'b0),
|
||||
.PHASESEL0(1'b0),
|
||||
.PHASESEL1(1'b0),
|
||||
.PHASEDIR(1'b0),
|
||||
.PHASESTEP(1'b0),
|
||||
.PLLWAKESYNC(1'b0),
|
||||
.ENCLKOP(1'b0),
|
||||
.ENCLKOS(1'b0),
|
||||
.ENCLKOS2(1'b0),
|
||||
.ENCLKOS3(1'b0),
|
||||
.LOCK(locked),
|
||||
.INTLOCK(int_locked)
|
||||
);
|
||||
endmodule
|
||||
|
||||
|
||||
module vgatestsrc(i_pixclk, i_reset,
|
||||
// External connections
|
||||
i_width, i_height,
|
||||
i_rd, i_newline, i_newframe,
|
||||
// VGA connections
|
||||
o_pixel);
|
||||
parameter BITS_PER_COLOR = 4,
|
||||
HW=12, VW=12;
|
||||
//HW=13,VW=11;
|
||||
localparam BPC = BITS_PER_COLOR,
|
||||
BITS_PER_PIXEL = 3 * BPC,
|
||||
BPP = BITS_PER_PIXEL;
|
||||
//
|
||||
input wire i_pixclk, i_reset;
|
||||
input wire [HW-1:0] i_width;
|
||||
input wire [VW-1:0] i_height;
|
||||
//
|
||||
input wire i_rd, i_newline, i_newframe;
|
||||
//
|
||||
output reg [(BPP-1):0] o_pixel;
|
||||
|
||||
|
||||
|
||||
wire [BPP-1:0] white, black, purplish_blue, purple, dark_gray,
|
||||
darkest_gray, mid_white, mid_cyan, mid_magenta,
|
||||
mid_red, mid_green, mid_blue, mid_yellow;
|
||||
wire [BPC-1:0] midv, mid_off;
|
||||
|
||||
assign midv = { 2'b11, {(BPC-2){1'b0}} };
|
||||
assign mid_off = { (BPC){1'b0} };
|
||||
|
||||
assign white = {(BPP){1'b1}};
|
||||
assign black = {(BPP){1'b0}};
|
||||
assign purplish_blue = {
|
||||
{(BPC){1'b0}},
|
||||
3'b001, {(BPC-3){1'b0}},
|
||||
2'b01, {(BPC-2){1'b0}} };
|
||||
assign purple = { {2'b00, {(BPC-2){1'b1}} }, {(BPC){1'b0}},
|
||||
{ 1'b0, {(BPC-1){1'b1}} } };
|
||||
|
||||
assign dark_gray = {(3){ { 4'b0010, {(BPC-4){1'b0}} } }};
|
||||
assign darkest_gray = {(3){ { 4'b0001, {(BPC-4){1'b0}} } }};
|
||||
|
||||
assign mid_white = { midv, midv, midv };
|
||||
assign mid_yellow = { midv, midv, mid_off };
|
||||
assign mid_red = { midv, mid_off, mid_off };
|
||||
assign mid_green = { mid_off, midv, mid_off };
|
||||
assign mid_blue = { mid_off, mid_off, midv };
|
||||
assign mid_cyan = { mid_off, midv, midv };
|
||||
assign mid_magenta = { midv, mid_off, midv };
|
||||
|
||||
reg [HW-1:0] hpos, hedge;
|
||||
reg [VW-1:0] ypos, yedge;
|
||||
reg [3:0] yline, hbar;
|
||||
//
|
||||
//
|
||||
// 1 Border
|
||||
// 8 BARS
|
||||
// 1 short bar
|
||||
// 3 fat bars
|
||||
// 1 border
|
||||
// 1 gradient bar
|
||||
// 1 border
|
||||
//
|
||||
reg dline;
|
||||
always @(posedge i_pixclk)
|
||||
if ((i_reset)||(i_newframe)||(i_newline))
|
||||
dline <= 1'b0;
|
||||
else if (i_rd)
|
||||
dline <= 1'b1;
|
||||
|
||||
always @(posedge i_pixclk)
|
||||
if ((i_reset)||(i_newframe))
|
||||
begin
|
||||
ypos <= 0;
|
||||
yline <= 0;
|
||||
yedge <= { 4'h0, i_height[(VW-1):4] };
|
||||
end else if (i_newline)
|
||||
begin
|
||||
ypos <= ypos + { {(VW-1){1'h0}}, dline };
|
||||
if (ypos >= yedge)
|
||||
begin
|
||||
yline <= yline + 1'b1;
|
||||
yedge <= yedge + { 4'h0, i_height[(VW-1):4] };
|
||||
end
|
||||
end
|
||||
|
||||
initial hpos = 0;
|
||||
initial hbar = 0;
|
||||
initial hedge = 0; // { 4'h0, i_width[(HW-1):4] };
|
||||
always @(posedge i_pixclk)
|
||||
if ((i_reset)||(i_newline))
|
||||
begin
|
||||
hpos <= 0;
|
||||
hbar <= 0;
|
||||
hedge <= { 4'h0, i_width[(HW-1):4] };
|
||||
end else if (i_rd)
|
||||
begin
|
||||
hpos <= hpos + 1'b1;
|
||||
if (hpos >= hedge)
|
||||
begin
|
||||
hbar <= hbar + 1'b1;
|
||||
hedge <= hedge + { 4'h0, i_width[(HW-1):4] };
|
||||
end
|
||||
end
|
||||
|
||||
reg [BPP-1:0] topbar, midbar, fatbar, gradient, pattern;
|
||||
always @(posedge i_pixclk)
|
||||
case(hbar[3:0])
|
||||
4'h0: topbar <= black;
|
||||
4'h1: topbar <= mid_white;
|
||||
4'h2: topbar <= mid_white;
|
||||
4'h3: topbar <= mid_yellow;
|
||||
4'h4: topbar <= mid_yellow;
|
||||
4'h5: topbar <= mid_cyan;
|
||||
4'h6: topbar <= mid_cyan;
|
||||
4'h7: topbar <= mid_green;
|
||||
4'h8: topbar <= mid_green;
|
||||
4'h9: topbar <= mid_magenta;
|
||||
4'ha: topbar <= mid_magenta;
|
||||
4'hb: topbar <= mid_red;
|
||||
4'hc: topbar <= mid_red;
|
||||
4'hd: topbar <= mid_blue;
|
||||
4'he: topbar <= mid_blue;
|
||||
4'hf: topbar <= black;
|
||||
endcase
|
||||
|
||||
always @(posedge i_pixclk)
|
||||
case(hbar[3:0])
|
||||
4'h0: midbar <= black;
|
||||
4'h1: midbar <= mid_blue;
|
||||
4'h2: midbar <= mid_blue;
|
||||
4'h3: midbar <= black;
|
||||
4'h4: midbar <= black;
|
||||
4'h5: midbar <= mid_magenta;
|
||||
4'h6: midbar <= mid_magenta;
|
||||
4'h7: midbar <= black;
|
||||
4'h8: midbar <= black;
|
||||
4'h9: midbar <= mid_cyan;
|
||||
4'ha: midbar <= mid_cyan;
|
||||
4'hb: midbar <= black;
|
||||
4'hc: midbar <= black;
|
||||
4'hd: midbar <= mid_white;
|
||||
4'he: midbar <= mid_white;
|
||||
4'hf: midbar <= black;
|
||||
endcase
|
||||
|
||||
always @(posedge i_pixclk)
|
||||
case(hbar[3:0])
|
||||
4'h0: fatbar <= black;
|
||||
4'h1: fatbar <= purplish_blue;
|
||||
4'h2: fatbar <= purplish_blue;
|
||||
4'h3: fatbar <= purplish_blue;
|
||||
4'h4: fatbar <= white;
|
||||
4'h5: fatbar <= white;
|
||||
4'h6: fatbar <= white;
|
||||
4'h7: fatbar <= purple;
|
||||
4'h8: fatbar <= purple;
|
||||
4'h9: fatbar <= purple;
|
||||
4'ha: fatbar <= darkest_gray;
|
||||
4'hb: fatbar <= black;
|
||||
4'hc: fatbar <= dark_gray;
|
||||
4'hd: fatbar <= darkest_gray;
|
||||
4'he: fatbar <= black;
|
||||
4'hf: fatbar <= black;
|
||||
endcase
|
||||
|
||||
reg [(HW-1):0] last_width;
|
||||
always @(posedge i_pixclk)
|
||||
last_width <= i_width;
|
||||
|
||||
// Attempt to discover 1/i_width in h_step
|
||||
localparam FRACB=16;
|
||||
//
|
||||
reg [(FRACB-1):0] hfrac, h_step;
|
||||
always @(posedge i_pixclk)
|
||||
if ((i_reset)||(i_newline))
|
||||
hfrac <= 0;
|
||||
else if (i_rd)
|
||||
hfrac <= hfrac + h_step;
|
||||
|
||||
always @(posedge i_pixclk)
|
||||
if ((i_reset)||(i_width != last_width))
|
||||
h_step <= 1;
|
||||
else if ((i_newline)&&(hfrac > 0))
|
||||
begin
|
||||
if (hfrac < {(FRACB){1'b1}} - { {(FRACB-HW){1'b0}}, i_width })
|
||||
h_step <= h_step + 1'b1;
|
||||
else if (hfrac < { {(FRACB-HW){1'b0}}, i_width })
|
||||
h_step <= h_step - 1'b1;
|
||||
end
|
||||
|
||||
always @(posedge i_pixclk)
|
||||
case(hfrac[FRACB-1:FRACB-4])
|
||||
4'h0: gradient <= black;
|
||||
// Red
|
||||
4'h1: gradient <= { 1'b0, hfrac[(FRACB-5):(FRACB-3-BPC)], {(2){mid_off}} };
|
||||
4'h2: gradient <= { 1'b1, hfrac[(FRACB-5):(FRACB-3-BPC)], {(2){mid_off}} };
|
||||
4'h3: gradient <= black;
|
||||
// Green
|
||||
4'h4: gradient <= { mid_off, 1'b0, hfrac[(FRACB-5):(FRACB-3-BPC)], mid_off };
|
||||
4'h5: gradient <= { mid_off, 1'b1, hfrac[(FRACB-5):(FRACB-3-BPC)], mid_off };
|
||||
4'h6: gradient <= black;
|
||||
// Blue
|
||||
4'h7: gradient <= { {(2){mid_off}}, 1'b0, hfrac[(FRACB-5):(FRACB-3-BPC)] };
|
||||
4'h8: gradient <= { {(2){mid_off}}, 1'b1, hfrac[(FRACB-5):(FRACB-3-BPC)] };
|
||||
4'h9: gradient <= black;
|
||||
// Gray
|
||||
4'ha: gradient <= {(3){ 2'b00, hfrac[(FRACB-5):(FRACB-2-BPC)] }};
|
||||
4'hb: gradient <= {(3){ 2'b01, hfrac[(FRACB-5):(FRACB-2-BPC)] }};
|
||||
4'hc: gradient <= {(3){ 2'b10, hfrac[(FRACB-5):(FRACB-2-BPC)] }};
|
||||
4'hd: gradient <= {(3){ 2'b11, hfrac[(FRACB-5):(FRACB-2-BPC)] }};
|
||||
4'he: gradient <= black;
|
||||
//
|
||||
4'hf: gradient <= black;
|
||||
endcase
|
||||
|
||||
always @(posedge i_pixclk)
|
||||
case(yline)
|
||||
4'h0: pattern <= black;
|
||||
4'h1: pattern <= topbar; //
|
||||
4'h2: pattern <= topbar;
|
||||
4'h3: pattern <= topbar;
|
||||
4'h4: pattern <= topbar;
|
||||
4'h5: pattern <= topbar;
|
||||
4'h6: pattern <= topbar;
|
||||
4'h7: pattern <= topbar;
|
||||
4'h8: pattern <= topbar;
|
||||
4'h9: pattern <= midbar; //
|
||||
4'ha: pattern <= fatbar; //
|
||||
4'hb: pattern <= fatbar;
|
||||
4'hc: pattern <= fatbar;
|
||||
4'hd: pattern <= black;
|
||||
4'he: pattern <= gradient;
|
||||
4'hf: pattern <= black;
|
||||
endcase
|
||||
|
||||
always @(posedge i_pixclk)
|
||||
if (i_newline)
|
||||
o_pixel <= white;
|
||||
else if (i_rd)
|
||||
begin
|
||||
if (hpos == i_width-12'd3)
|
||||
o_pixel <= white;
|
||||
else if ((ypos == 0)||(ypos == i_height-1))
|
||||
o_pixel <= white;
|
||||
else
|
||||
o_pixel <= pattern;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
module llhdmi(
|
||||
i_tmdsclk, i_pixclk,
|
||||
i_reset, i_red, i_grn, i_blu,
|
||||
o_rd, o_newline, o_newframe,
|
||||
`ifdef VERILATOR
|
||||
o_TMDS_red, o_TMDS_grn, o_TMDS_blu,
|
||||
`endif
|
||||
o_red, o_grn, o_blu);
|
||||
|
||||
input wire i_tmdsclk; // TMDS clock
|
||||
input wire i_pixclk; // Pixel clock, 10 times slower than i_tmdsclk
|
||||
input wire i_reset; // Reset this module when strobed high
|
||||
input wire [7:0] i_red; // Red green and blue colour values
|
||||
input wire [7:0] i_grn; // for each pixel
|
||||
input wire [7:0] i_blu;
|
||||
output wire o_rd; // True when we can accept pixel data
|
||||
output reg o_newline; // True on last pixel of each line
|
||||
output reg o_newframe; // True on last pixel of each frame
|
||||
output wire o_red; // Red TMDS pixel stream
|
||||
output wire o_grn; // Green TMDS pixel stream
|
||||
output wire o_blu; // Blue TMDS pixel stream
|
||||
`ifdef VERILATOR
|
||||
output wire [9:0] o_TMDS_red, o_TMDS_grn, o_TMDS_blu;
|
||||
assign o_TMDS_red= TMDS_red;
|
||||
assign o_TMDS_grn= TMDS_grn;
|
||||
assign o_TMDS_blu= TMDS_blu;
|
||||
`endif
|
||||
|
||||
reg [9:0] CounterX, CounterY;
|
||||
reg hSync, vSync, DrawArea;
|
||||
|
||||
// Keep track of the current X/Y pixel position
|
||||
always @(posedge i_pixclk)
|
||||
if (i_reset)
|
||||
CounterX <= 0;
|
||||
else
|
||||
CounterX <= (CounterX==799) ? 0 : CounterX+1;
|
||||
|
||||
always @(posedge i_pixclk)
|
||||
if (i_reset)
|
||||
CounterY <= 0;
|
||||
else if (CounterX==799) begin
|
||||
CounterY <= (CounterY==524) ? 0 : CounterY+1;
|
||||
end
|
||||
|
||||
// Signal end of line, end of frame
|
||||
always @(posedge i_pixclk) begin
|
||||
o_newline <= (CounterX==639) ? 1 : 0;
|
||||
o_newframe <= (CounterX==639) && (CounterY==479) ? 1 : 0;
|
||||
end
|
||||
|
||||
// Determine when we are in a drawable area
|
||||
always @(posedge i_pixclk)
|
||||
DrawArea <= (CounterX<640) && (CounterY<480);
|
||||
|
||||
assign o_rd= ~i_reset & DrawArea;
|
||||
|
||||
// Generate horizontal and vertical sync pulses
|
||||
always @(posedge i_pixclk)
|
||||
hSync <= (CounterX>=656) && (CounterX<752);
|
||||
|
||||
always @(posedge i_pixclk)
|
||||
vSync <= (CounterY>=490) && (CounterY<492);
|
||||
|
||||
// Convert the 8-bit colours into 10-bit TMDS values
|
||||
wire [9:0] TMDS_red, TMDS_grn, TMDS_blu;
|
||||
TMDS_encoder encode_R(.clk(i_pixclk), .VD(i_red), .CD(2'b00),
|
||||
.VDE(DrawArea), .TMDS(TMDS_red));
|
||||
TMDS_encoder encode_G(.clk(i_pixclk), .VD(i_grn), .CD(2'b00),
|
||||
.VDE(DrawArea), .TMDS(TMDS_grn));
|
||||
TMDS_encoder encode_B(.clk(i_pixclk), .VD(i_blu), .CD({vSync,hSync}),
|
||||
.VDE(DrawArea), .TMDS(TMDS_blu));
|
||||
|
||||
// Strobe the TMDS_shift_load once every 10 i_tmdsclks
|
||||
// i.e. at the start of new pixel data
|
||||
reg [3:0] TMDS_mod10=0;
|
||||
reg TMDS_shift_load=0;
|
||||
always @(posedge i_tmdsclk) begin
|
||||
if (i_reset) begin
|
||||
TMDS_mod10 <= 0;
|
||||
TMDS_shift_load <= 0;
|
||||
end else begin
|
||||
TMDS_mod10 <= (TMDS_mod10==4'd9) ? 4'd0 : TMDS_mod10+4'd1;
|
||||
TMDS_shift_load <= (TMDS_mod10==4'd9);
|
||||
end
|
||||
end
|
||||
|
||||
// Latch the TMDS colour values into three shift registers
|
||||
// at the start of the pixel, then shift them one bit each i_tmdsclk.
|
||||
// We will then output the LSB on each i_tmdsclk.
|
||||
reg [9:0] TMDS_shift_red=0, TMDS_shift_grn=0, TMDS_shift_blu=0;
|
||||
always @(posedge i_tmdsclk) begin
|
||||
if (i_reset) begin
|
||||
TMDS_shift_red <= 0;
|
||||
TMDS_shift_grn <= 0;
|
||||
TMDS_shift_blu <= 0;
|
||||
end else begin
|
||||
TMDS_shift_red <= TMDS_shift_load ? TMDS_red: {1'b0, TMDS_shift_red[9:1]};
|
||||
TMDS_shift_grn <= TMDS_shift_load ? TMDS_grn: {1'b0, TMDS_shift_grn[9:1]};
|
||||
TMDS_shift_blu <= TMDS_shift_load ? TMDS_blu: {1'b0, TMDS_shift_blu[9:1]};
|
||||
end
|
||||
end
|
||||
|
||||
// Finally output the LSB of each color bitstream
|
||||
assign o_red= TMDS_shift_red[0];
|
||||
assign o_grn= TMDS_shift_grn[0];
|
||||
assign o_blu= TMDS_shift_blu[0];
|
||||
|
||||
endmodule
|
||||
|
||||
module TMDS_encoder(
|
||||
input clk, // 250 MHz
|
||||
input [7:0] VD, // video data (red, green or blue)
|
||||
input [1:0] CD, // control data
|
||||
input VDE, // video data enable, to choose between CD (when VDE=0) and VD (when VDE=1)
|
||||
output reg [9:0] TMDS = 0
|
||||
);
|
||||
|
||||
wire [3:0] Nb1s = {3'b0, VD[0]} + {3'b0, VD[1]} + {3'b0, VD[2]}
|
||||
+ {3'b0, VD[3]} + {3'b0, VD[4]} + {3'b0, VD[5]}
|
||||
+ {3'b0, VD[6]} + {3'b0, VD[7]};
|
||||
wire XNOR = (Nb1s>4'd4) || (Nb1s==4'd4 && VD[0]==1'b0);
|
||||
|
||||
// To keep Verilator happy, we create individual wires, determine
|
||||
// their values and then merge them into q_m[]
|
||||
wire QM0, QM1, QM2, QM3, QM4, QM5, QM6, QM7, QM8;
|
||||
assign QM0= VD[0];
|
||||
assign QM1= QM0 ^ VD[1] ^ XNOR;
|
||||
assign QM2= QM1 ^ VD[2] ^ XNOR;
|
||||
assign QM3= QM2 ^ VD[3] ^ XNOR;
|
||||
assign QM4= QM3 ^ VD[4] ^ XNOR;
|
||||
assign QM5= QM4 ^ VD[5] ^ XNOR;
|
||||
assign QM6= QM5 ^ VD[6] ^ XNOR;
|
||||
assign QM7= QM6 ^ VD[7] ^ XNOR;
|
||||
assign QM8= ~XNOR;
|
||||
wire [8:0] q_m = { QM8, QM7, QM6, QM5, QM4, QM3, QM2, QM1, QM0 };
|
||||
|
||||
reg [3:0] balance_acc = 0;
|
||||
wire [3:0] balance = {3'b0, q_m[0]} + {3'b0, q_m[1]} + {3'b0, q_m[2]}
|
||||
+ {3'b0, q_m[3]} + {3'b0, q_m[4]} + {3'b0, q_m[5]}
|
||||
+ {3'b0, q_m[6]} + {3'b0, q_m[7]} - 4'd4;
|
||||
wire balance_sign_eq = (balance[3] == balance_acc[3]);
|
||||
wire invert_q_m = (balance==0 || balance_acc==0) ? ~q_m[8] : balance_sign_eq;
|
||||
|
||||
wire [3:0] balance_acc_inc = balance
|
||||
- {3'b0,
|
||||
({q_m[8] ^ ~balance_sign_eq} & ~(balance==0 || balance_acc==0)) };
|
||||
wire [3:0] balance_acc_new = invert_q_m ? balance_acc-balance_acc_inc : balance_acc+balance_acc_inc;
|
||||
wire [9:0] TMDS_data = {invert_q_m, q_m[8], q_m[7:0] ^ {8{invert_q_m}}};
|
||||
wire [9:0] TMDS_code = CD[1] ? (CD[0] ? 10'b1010101011 : 10'b0101010100) : (CD[0] ? 10'b0010101011 : 10'b1101010100);
|
||||
|
||||
always @(posedge clk) TMDS <= VDE ? TMDS_data : TMDS_code;
|
||||
always @(posedge clk) balance_acc <= VDE ? balance_acc_new : 4'h0;
|
||||
endmodule
|
Loading…
Reference in a new issue