0% found this document useful (0 votes)
1 views30 pages

Structvhdl

The document presents a structured VHDL design method that contrasts with traditional ad-hoc design styles, which are often difficult to read and maintain. It advocates for a two-process scheme that separates combinational and sequential logic, improving readability, synthesis speed, and maintenance. The method emphasizes the use of records and higher abstraction levels to enhance code clarity and facilitate debugging.

Uploaded by

dltailieu
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
1 views30 pages

Structvhdl

The document presents a structured VHDL design method that contrasts with traditional ad-hoc design styles, which are often difficult to read and maintain. It advocates for a two-process scheme that separates combinational and sequential logic, improving readability, synthesis speed, and maintenance. The method emphasizes the use of records and higher abstraction levels to enhance code clarity and facilitate debugging.

Uploaded by

dltailieu
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 30

A Structured VHDL Design Method

Jiri Gaisler

CTH / Gaisler Research


Outline of lecture

Traditional 'ad-hoc' VHDL design style


Proposed structured design method
Various ways of increasing abstraction level in
synthesisable code
A few design examples
Traditional VHDL design methodology

Based on heritage from schematic entry (!):


Many small processes or concurrent statements
Use of TTL-like macro blocks
Use of GUI tools for code-generation
Could be compared to schematic without wires (!)
Hard to read due to many concurrent statements
Auto-generated code even harder to read/maintain
Hard to read = difficult to maintain
Traditional ad-hoc design style

Many concurrent statments


Many signal
Few and small process statements
No unified signal naming convention
Coding is done at low RTL level:
Assignments with logical expressions
Only simple array data structures are used
Simple VHDL example
CbandDatat_LatchPROC9F: process(MDLE, CB_In, Reset_Out_N)

begin
if Reset_Out_N = '0'then
CBLatch_F_1 <= "0000";
elsif MDLE = '1'then
CBLatch_F_1 <= CB_In(3 downto 0);
end if;
end process;

CBandDatat_LatchPROC10F: process(MDLE, CB_In, DParIO_In, Reset_Out_N)


begin
if Reset_Out_N = '0'then
CBLatch_F_2 <= "0000";
elsif MDLE = '1'then
CBLatch_F_2(6 downto 4) <= CB_In(6 downto 4);
CBLatch_F_2(7) <= DParIO_In;
end if;
end process;

CBLatch_F <= CBLatch_F_2 & CBLatch_F_1;


Problems

Dataflow coding difficult to understand


Algorithm difficult to understand
No distinction between sequential and comb. signals
Difficult to identify related signals
Large port declarations in entity headers
Slow execution due to many signals and processes

The ad-hoc style does not scale


Ideal model characteristics

We want our models to be:


Easy to understand and maintain
Synthesisable
Simulate as fast as possible
No simulation/synthesis discrepancies
Usable for small and large designs

New design style/method needed !


1: abstraction of digital logic
A synchronous design can be abstracted into two
separate parts; a combinational and a sequential

q
Comb
d q = f(d,qr)
DFF

qr

Clk
2: the abstracted view in VHDL
The two-process scheme
A VHDL entity is made to contain only two processes: one


sequential and one combinational


Inputs are denoted d, outputs q


Two local signals are declared:




register-in (ri) and register-out (r)


The full algorithm (q = f(d,r))is performed in the


combinational process
The combinational process is sensitive to all input ports and


the register outputs r


The sequential process is only sensitive to the clock

Two-process VHDL entity

Q Out-port
In-ports Comb. Pro.
d q = f1(d,r)
ri = f2(d,r) ri Seq.
Process

r
Clk
Two-process scheme: data types

The local signals r and rin are of composite type (record) and


include all registered values


All outputs are grouped into one entity-specific record type,


declared in a global interface package


Input ports can be of output record types from other entities


All registers declared in a record type




A local variable of the register record type is declared in the




combinational processes to hold newly calculated values


Additional variables of any type can be declared in the


combinational process to hold temporary values


Example
use work.interface.all; begin

entity irqctrl is port ( comb : process (sysif, r)


clk : in std_logic; variable v : reg_type;
rst : in std_logic; begin
sysif : in sysif_type; v := r; v.irq := '0';
irqo : out irqctrl_type); for i in r.pend'range loop
end; v.pend := r.pend(i) or
(sysif.irq(i) and r.mask(i));
architecture rtl of irqctrl is v.irq := v.irq or r.pend(i);
end loop;
type reg_type is record rin <= v;
irq : std_logic; irqo.irq <= r.irq;
pend : std_logic_vector(0 to 7); end process;
mask : std_logic_vector(0 to 7);
end record;
reg : process (clk)
signal r, rin : reg_type; begin
if rising_edge(clk) then
r <= rin;
end if;
end process;

end architecture;
Hierarchical design
use work.interface.all;
Grouping of signals makes


entity cpu is port (


code readable and shows the clk : in std_logic;
direction of the dataflow rst : in std_logic;
mem_in : in mem_in_type;
mem_out : out mem_out_type);
end;
Clk, rst
Proc architecture rtl of cpu is
signal cache_out : cache_type;
signal proc_out : proc_type;
signal mctrl_out : mctrl_type;
begin
Cache
u0 : proc port map
(clk, rst, cache_out, proc_out);

u1 : cache port map


Mctrl (clk, rst, proc_out, mem_out cache_out);

u2 : mctrl port map


(clk, rst, cache_out, mem_in, mctrl_out,
mem_out);

end architecture;
Memory
Benefits

Sequential coding is well known and understood




Algorithm easily extracted




Uniform coding style simplifies maintenance




Improved simulation and synthesis speed




Development of models is less error-prone



Adding an port

Traditional method: Two-process method:





Add port in entity port Add element in the interface



declaration record
Add port in sensitivity list of


appropriate processes (input


ports only)
Add port in component


declaration
Add signal declaration in parent


module(s)
Add port map in component


instantiation in parent module(s)


Adding a register

Traditional method: Two-process method:





Add signal declaration (2 sig.) Add definition in register record



Add registered signal in process


sensitivity list (if not implicite)


(Declare local variable)


Add driving statement in clocked




process
Tracing signals during debugging

Traditional method: Two-process method:





Figure out which signals are Add interface records, r and rin



registered, which are their inputs,
Signals are grouped according to


and how they are functionally
related function and easy to understand
Addition/deletion of record


Add signals to trace file


elements automatically
Repeat every time a port or propagated to trace window


register is added/deleted
Stepping through code during debugging

Traditional method: Two-process method:





Connected processes do not Add a breakpoint in the begining



execute sequentially due to delta of the combinational process
signal delay
Single-step through code to


A breakpoint in every connected execute complete algorithm


process needed
Next signal value (ri) directly


New signal value in concurrent visible in variable v


processes not visible


Complete algorithm can be a sub-program

Allows re-use if placed in a comb : process (sysif, r, rst)




variable v : reg_type;
global package (e.g. EDAC) begin

Can be verified quickly with proc_irqctl(sysif, r, v);




local test-bench rin <= v;


irqo.irq <= r.irq;
Meiko FPU (20 Kgates):


end process;

1 entity, 2 processes


44 sub-programs


13 signal assignments


Reverse-engineered from


verilog: 87 entities, ~800


processes, ~2500 signals
Sequential code and synthesis

Most sequential statements comb : process (sysif, r, rst)




variable v : reg_type;
directly synthesisable by modern begin
tools
proc_irqctl(sysif, r, v);
All variables have to be assigned


if rst = '1' then


to avoid latches v.irq := '0';
v.pend := (others => '0');
Order of code matters! end if;


rin <= v;
Avoid recursion, division, access


irqo.irq <= r.irq;


types, text/file IO. end process;
Comparison MEC/LEON

ERC32 memory contoller MEC LEON SPARC-V8 processor





Ad-hoc method (15 designers) Two-process method (mostly)



25,000 lines of code 15,000 lines of code



45 entities, 800 processes 37 entities, 75 processes



2000 signals 300 signals



3000 signal assigments 800 signal assigments



30 Kgates, 10 man-years, 100 Kgates, 2 man-years, no


numerous of bugs, 3 iterations bugs in first silicon


Increasing the abstraction level

Benefits Problems



Easier to understand the Keep the code synthesisable



underlying algorithm
Synthesis tool might choose


Easier to modify/maintain wrong gate-level structure


Faster simulation Problems to understand





algorithm for less skilled
Use built-in module


engineers
generators (synthesis)
Using records

Useful to group related signals type reg1_type is record




f1 : std_logic_vector(0 to 7);
f2 : std_logic_vector(0 to 7);
Nested records further improves


f3 : std_logic_vector(0 to 7);
readability end record;

Directly synthesisable type reg2_type is record




x1 : std_logic_vector(0 to 3);
x2 : std_logic_vector(0 to 3);
Element name might be difficult


x3 : std_logic_vector(0 to 3);
to find in synthesised netlist end record;

type reg_type is record


reg1 : reg1_type;
reg2 : reg2_type;
end record;

variable v : regtype;

v.reg1.f3 := “0011001100”;
Using ieee.std_logic_arith.all;

Written by Synopsys, now freely type unsigned is array (natural range




<>) of st_logic;
available type signed is array (natural range
<>) of st_logic;
Declares to additional types:


signed and unsigned variable u1, u2, u3 : unsigned;


variable v1 : std_logic_vector;
Declares arithmetic and various


conversion operators: +, -, *, /, <,


u1 := u1 + (u2 * u3);
>, =, <=, >=, /=, conv_integer
if (v1 >= v2) then ...
Built-in, optimised versions


v1(0) := u1(conv_integer(u2));
available in all simulators and
synthesis tools
IEEE alternative: numeric_std

Use of loops

Used for iterative calculations variable v1 : std_logic_vector(0 to 7);




variable first_bit : natural;


Index variable implicitly


-- find first bit set


declared for i in v1'range loop
if v1(i) = '1' then
Typical use: iterative algorithms, first_bit := i; exit;


end if;
priority encoding, sub-bus end loop;
extraction, bus turning
-- reverse bus
for in 0 to 7 loop
v1(i) := v2(7-i);
end loop;
Multiplexing using integer conversion

N to 1 multiplexing function genmux(s, v : std_logic_vector)




return std_logic is
variable res : std_logic_vector(v'length-1
downto 0);
variable i : integer;
begin
res := v; -- needed to get correct index
i := conv_integer(unsigned(s));
return(res(i));
end;

function decode(v : std_logic_vector) return


N to 2**N decoding


std_logic_vector is
variable res :
std_logic_vector((2**v'length)-1 downto 0);
variable i : natural;
begin
res := (others => '0');
i := conv_integer(unsigned(v));
res(i) := '1';
return(res);
end;
State machines
architecture rtl of mymodule is
Simple case-statement type state_type is (first, second, last);


type reg_type is record


implementation state : state_type;
drive : std_logic;
Maintains current state


end record;
signal r, rin : reg_type;
Both combinational and begin


comb : process(...., r)
registered output possible begin
case r.state is
when first =>
if cond0 then v.state := second; end if;
when second =>
if cond1 then v.state := first;
elsif cond2 then v.state := last; end if;
when others =>
v.drive := '1'; v.state := first;
end case;
if reset = '1' then v.state := first; end if;
modout.cdrive <= v.drive; -- combinational
modout.rdrive <= r.drive; -- registered
end process;
.
Conclusions

The two-process design method provides a uniform




structure, and a natural division between algorithm


and state
It improves


Development time (coding, debug)




Simulation and synthesis speed




Readability


Maintenance and re-use




You might also like