User HDL
The original custom-HDL block: one VHDL entity, typed into a built-in editor and stored inside the project, with its ports declared by hand in a small table. The compiler renames the entity to a unique name, writes it into the project’s pcores folder and instantiates it with the pins you declared. Still fully functional, but superseded by Custom HDL (NEW).
Introduction
Principle of Operation
User HDL lets you drop a hand-written VHDL entity straight into a schematic. You do three things:
- Type (or paste) one VHDL file into the built-in editor.
- Name the entity in that file — the Main Entity Name.
- Declare that entity’s ports, one row per port, in the table next to the editor.
The block then draws itself: one pin per declared port, input pins on the left,
output pins on the right. At compile time the code is written out into the
project’s pcores folder and instantiated, with your pins connected to the ports
of the same name.
The code lives inside the project file (base64-encoded in a property), so a project is self-contained: no external folder to keep in step.
Relationship to Custom HDL (NEW)
This block predates Custom HDL (NEW) (Custom HDL (NEW), version 2.0.0.0) and
both are still registered and shipped in the same plugin. Nothing was removed and
nothing was deprecated in code: a design that uses User HDL keeps compiling
exactly as before.
That said, the newer block is a strict superset for anything new, and the differences are not cosmetic:
| User HDL (this block) | Custom HDL (NEW) | |
|---|---|---|
| Files per block | exactly one — only the top file is written out | many; sub-components are written alongside the top |
| Generics | none — no generic is ever passed to your entity | INTEGER, BOOLEAN, STRING, STD_LOGIC, STD_LOGIC_VECTOR, TIME, REAL |
| Verilog | listed in the property, but the file is always written as .vhd |
written as .vhd or .v following the Language setting |
| Text encoding | ASCII — non-ASCII characters in your code are mangled | UTF-8 |
| Entity-end syntax accepted | only end <name>; / end <name> ; |
also end entity <name>; and bare end entity; |
| Port discovery | typed by hand into the table | typed by hand or scanned out of the HDL |
| Syntax checking | none | GHDL, in the editor |
| Editor | Scintilla, single buffer | Monaco, file tree, hierarchy view |
Which one to use: for a new design, use Custom HDL (NEW). Reach for User HDL only when you are maintaining an existing block, or when you want the simplest possible thing: one small entity, no generics, no sub-components. There is no automatic converter — migrating means creating a new block and re-entering the code and the port table.
If you would rather keep the HDL in files on disk, under your own version control, use User HDL Link instead: same idea, but the sources stay in an external folder.
Pin Description
All pins on this block come from the port table. There are no fixed pins: the symbol is rebuilt from the stored port list every time the interface changes, one pin per row, named after the Port Name column and placed left or right according to Direction.
Pin width follows Port Size, except:
- Any Size rows take the width of the net they are wired to;
STD_LOGIC_VECTOR_TMrows are instantiated as a flat vector ofPort Size × Time Multiplexingbits.
An unconnected input pin is not an error. What it is tied to depends on the
Function column: the typed Default Value for Port, the project
acquisition clock for Clock, the project global reset for Reset.
Properties
JSON with list of interfaces
The port table, serialised as JSON. Each entry carries PortName, PortType
(Port / Clock / Reset), Size, Direction, DataType,
TimeMultiplexing, AnySize and DataDefault.
Managed by the HDL editor — edit it there, not in the property grid. Changing it rebuilds the symbol.
Default: empty — a block with no ports draws as an empty box.
VHDL/Verilog user code (encoded in base64)
The HDL source itself: a JSON container holding one file whose content is base64-encoded ASCII, plus the top-file index and the language.
Despite the name this is the code, not the entity name — that one is in
EntityName.
Managed by the HDL editor.
Default: empty
Select between VHDL and Verilog
Language selector for the editor’s syntax colouring and for the stored file type.
| Value | Effect |
|---|---|
| VHDL | the intended, fully supported setting |
| Verilog | selectable, but the compiler still writes the file as .vhd |
Use Custom HDL (NEW) if you need a Verilog source to be emitted as .v.
Default: empty (the editor opens on VHDL)
Options: VHDL Verilog
Select between text input and file mode
Intended to switch the block between typing code into the editor (Text) and
referencing files (File).
The legacy editor only ever reports Text, so File is never actually
selected and the property has no effect on what is compiled. For the
file-on-disk workflow use the User HDL Link block.
Default: empty (behaves as Text)
Options: Text File
JSON array list of file in the component
A JSON array of files, kept for the unimplemented File mode. It is never
read by the compiler and never written by the editor.
Default: empty
Specify the entity in the code that is bounded to the I/O of the block
The Main Entity Name: the name of the entity in your code that this block
binds its pins to. It must match the entity … is, end …; and
architecture … of … is lines in the source, and it is also what every
<NAME> token in the code is replaced with when you save.
It becomes part of the emitted file name and of the unique entity name
(<designator>_UserHDL_<EntityName>), and it is shown in the block caption as
HDL - <EntityName>.
Changing it rebuilds the symbol. The editor refuses to save while it is empty.
Default: empty
⚙️ Detailed Operation
The editor
Double-click the block to open the HDL editor. It has three parts:
- a Scintilla code editor with VHDL syntax colouring;
- a Main Entity Name box and a Code Language selector (
VHDL/Verilog); - the port table, with Add / Delete buttons.
Editor shortcuts:
| Keys | Action |
|---|---|
Ctrl+F |
incremental search |
Ctrl+Shift+F |
find all |
F3 / Shift+F3 |
find next / previous |
Ctrl+H |
replace |
Ctrl+G |
go to line |
Ctrl+Q / Ctrl+W |
comment / uncomment selection |
A convenience worth knowing: on save, every occurrence of the literal token
<NAME> in your code is replaced by the text in the Main Entity Name box.
Writing entity <NAME> is … end <NAME>; means you only have to rename the block
in one place.
Saving is refused, with You must define an entity as TOP, if the Main Entity
Name box is empty.
The port table
One row per port of your entity. The columns map one-to-one onto what the compiler does:
| Column | Values | Effect |
|---|---|---|
| Function | Port, Clock, Reset |
what to connect when the pin is left open — see below |
| Port Name | text | must match the port name in your VHDL, and becomes the pin caption |
| Port Size | integer | bit width |
| Direction | IN, OUT |
pin side |
| Type | STD_LOGIC, STD_LOGIC_VECTOR, STD_LOGIC_VECTOR_TM, INTEGER |
the VHDL type of the port |
| Any Size | checkbox | width is taken from whatever net you wire to the pin |
| Time Multiplexing | integer | TM factor, only meaningful for STD_LOGIC_VECTOR_TM |
| Default Value | text | what an unconnected Port input is tied to |
A new row is created as Port / PORT1 / 32 / IN / STD_LOGIC_VECTOR / not any-size / TM 0 / no default.
Function decides what happens when a pin is left unconnected on the schematic:
Port— the pin is tied to the Default Value you typed.Clock— the pin is tied to the project’s acquisition clock.Reset— the pin is tied to the project’s global reset.
Clock and Reset ports must be STD_LOGIC, or STD_LOGIC_VECTOR of size 1.
Anything else is a compile error (see the message table below).
Time-multiplexed ports. A port declared STD_LOGIC_VECTOR_TM with a TM factor
of k appears on the symbol as a TM pin, but is instantiated as a plain
STD_LOGIC_VECTOR of Port Size × k bits. Your entity must declare it that
way: the TM packing is a schematic-level convention, the block passes the flat
vector.
Any Size. Ticking it makes the pin adopt the width of the net it is connected to. Your entity still has to be able to take that width — there are no generics in this block, so in practice this is only useful for a port whose width you already know matches.
What the compiler does
For a block with designator U12, component name UserHDL and Main Entity Name
my_filter, the compiler:
-
Base64-decodes your file (as ASCII).
-
Collapses every run of two or more spaces into one. This normalised text is both what the parser searches and, importantly, what is written to disk — so aligned columns in your source come out squashed in the generated file. Tabs and newlines are untouched.
-
Checks that all three of the following appear, case-insensitively:
entity my_filter is end my_filter; (or end my_filter ;) of my_filter is -
Renames all three to the unique name
U12_UserHDL_my_filter, so two copies of the same block in one design do not collide. -
Writes
pcores/U12_UserHDL_my_filter.vhd. -
Emits the instantiation, connecting each declared port to its pin (or to the default / clock / reset if the pin is open).
Compiler messages
| Message | Type | Meaning |
|---|---|---|
<unique name> Connecting signal: <port> |
info | one line per port that was wired |
Unable to find the entity: <name>. Please verify your HDL custom code. This parser has looked for the following string in your code: |
error | followed by the three patterns it looked for and the line If even one is missing or mispelled the compilation of custom HDL block is halted by the parser |
Unable to find the entity: <name>. Clock signal must be or STD_LOGIC or STD_LOGIC_VECTOR with size 1 |
error | a port with Function = Clock has the wrong type or a width other than 1 |
Unable to find the entity: <name>. Reset signal must be or STD_LOGIC or STD_LOGIC_VECTOR with size 1 |
error | same, for Function = Reset |
Error occoured in custom HDL block. Error message is: <exception> |
error | the stored JSON could not be decoded — usually a block whose editor was never opened, or corrupted properties |
Example
A small counter, written so that the <NAME> trick does the renaming:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity <NAME> is
Port (
CLK : in STD_LOGIC;
RESET : in STD_LOGIC;
ENABLE : in STD_LOGIC_VECTOR(0 downto 0);
COUNT : out STD_LOGIC_VECTOR(15 downto 0)
);
end <NAME>;
architecture Behavioral of <NAME> is
signal cnt : unsigned(15 downto 0) := (others => '0');
begin
process(CLK)
begin
if rising_edge(CLK) then
if RESET = '1' then
cnt <= (others => '0');
elsif ENABLE(0) = '1' then
cnt <= cnt + 1;
end if;
end if;
end process;
COUNT <= std_logic_vector(cnt);
end Behavioral;
The matching port table:
| Function | Port Name | Size | Direction | Type | Any | TM | Default |
|---|---|---|---|---|---|---|---|
| Clock | CLK |
1 | IN | STD_LOGIC | 0 | ||
| Reset | RESET |
1 | IN | STD_LOGIC | 0 | ||
| Port | ENABLE |
1 | IN | STD_LOGIC_VECTOR | 0 | "1" |
|
| Port | COUNT |
16 | OUT | STD_LOGIC_VECTOR | 0 |
With CLK and RESET marked as Clock and Reset, those two pins can simply be
left open on the schematic and they will pick up the project clock and reset.
Where it can be used
The block is available on every board, on subpages, and inside state machines. It owns no endpoint and no registers: it is pure logic, invisible to software.
Quick reference
| Item | Value |
|---|---|
| Files | one, stored in the project (base64, ASCII) |
| Generics | not supported |
| Emitted file | pcores/<designator>_UserHDL_<entity>.vhd |
| Entity renaming | automatic, so the block can be instantiated many times |
| Registers / endpoint | none |
| Successor | Custom HDL (NEW) — use it for new designs |
Resources & Timing
- The block adds no logic of its own: resource usage is exactly that of the entity you wrote, plus any width adaptation the schematic wiring needs.
- The source is written to
pcores/on every compile, so editing the block is enough — there is nothing to copy by hand. - The generated file is the whitespace-normalised copy of your code (runs of two or more spaces collapsed to one), not a byte-for-byte copy.
- No endpoint, no registers, no SDK output.