Feineigle.com - The Designer's Guide to VHDL

Home · Book Reports · 2018 · The Designer's Guide to VHDL

Published: February 1, 2018
Tags:  Electronics · Programming · VHDL



The book in...
One sentence:
A seemingly exhaustive look at the features of VHDL with plenty of examples and a few case studies.

Five sentences:
The Designer's Guide starts off with pretty basic concepts such as data types and logic, both combinational and sequential. Next it moves into modeling, procedures, functions, components, aliasing, generate, and generic statements. It discusses a number of more exotic features, like configurations as well as access types that have limitations, if even available, during synthesis. Included are a few case studies, one on DSP pipelining, another on memory design, and a final one on the gumnut, a simple soft core processor. All in all it is a great reference manual for VHDL, assuming you already have knowledge of the digital design process and know what you want to implement.

designates my notes. / designates important.


Thoughts

The main shortcoming is that there is, at least in my digital copy, no table of contents. This is rather horrible considering that it is such a great reference.

It covers, in depth, many, if not most, of constructs you would use in VHDL. Beyond what you would commonly use, it also covers the more esoteric topics like encrypting intellectual property.

Some of the topics are not of use to a hobbyist level tinkerer. In particular some of the high performance timing and simulation only features would unlikely be used by anyone but a professional. Still, they are worthwhile reading over to give a better understanding of the language’s full capability.

Each chapter ends with a number of questions, ranked in difficulty between 1 and 5. The difficulty 1 questions are answered in an appendix. This is an extremely nice feature for someone learning on their own.


Table of Contents


· Chapter 01: Fundamental Concepts

page 11:
page 12:
page 26:
page 30:
46E5 1E+12 19e00
1.234E09 98.6E+21 34.0e-08
page 31:
"A string in a string: ""A string"". "
"If a string will not fit on one line, "
& "then we can break it into parts on separate lines."
page 33:

· Chapter 02: Scalar Data Types and Operations

page 43:
package int_types is
  type small_int is range 0 to 255;
end package int_types;
use work.int_types.all;
entity small_adder is
  port (a, b : in small_int;
        s : out small_int
       );
end entity small_adder;
page 45:
maximum(3, 20) = 20
minimum(3, 20) = 3
page 57:
'1' ?= 'H' = '1'
'0' ?= 'H' = '0'
'1' ?/= 'H' = '0'
'0' ?/= 'H' = '1'
'Z' ?= 'H' = 'X'
'0' ?= 'U' = 'U'
'1' ?= '-' = '1'
'W' ?/= 'H' = 'X'
'0' ?/= 'U' = 'U'
-' ?/= '-' = '0'
page 59:
page 62:
T'left      first (leftmost) value in T
T'right     last (rightmost) value in T
T'low       least value in T
T'high      greatest value in T
T'ascending true if T is an ascending range, false otherwise
T'image(x)  a string representing the value of x
T'value(s)  the value in T that is represented by s

T'pos(x)     position number of x in T
T'val(n)     value in T at position n
T'succ(x)    value in T at position one greater than that of x
T'pred(x)    value in T at position one less than that of x
T'leftof(x)  value in T at position one to the left of x
T'rightof(x) value in T at position one to the right of x

T'base       For any subtype T, this attribute produces the base type of T

· Chapter 03: Sequential Statements

page 92:
for count_value in 0 to 127 loop
  count_out <= count_value;
  wait for 5 ns;
end loop; 

· Chapter 04: Composite Data Types and Operations

page 111:
A'left(N)      Left bound of index range of dimension N of A
A'right(N)     Right bound of index range of dimension N of A
A'low(N)       Lower bound of index range of dimension N of A
A'high(N)      Upper bound of index range of dimension N of A
A'range(N)     Index range of dimension N of A
A'reverse_     range(N) Reverse of index range of dimension N of A
A'length(N)    Length of index range of dimension N of A
A'ascending(N) true if index range of dimension N of A is an ascending range, false otherwise
A'element      The element subtype of A
page 113:
type sample is array (natural range <>) of integer;
variable short_sample_buf : sample(0 to 63);

-- or

subtype long_sample is sample(0 to 255);
variable new_sample_buf, old_sample_buf : long_sample;

-- or

constant lookup_table : sample:= ( 1 => 23, 3 => -16, 2 => 100, 4 => 11);
page 118:
variable main_sample_set : sample_set(1 to 100)(1 to 20);
variable bakers_samples : dozen_samples(open)(0 to 9);
page 119:
constant default_sample_pair : sample_set
          := (1 => (39, 25, 6, -4, 5),
              2 => (9, 52, 100, 0, -1),
              3 => (0, 0, 0, 0, 0) 
             );
page 136:
type time_stamp is record
seconds : integer range 0 to 59;
minutes : integer range 0 to 59;
hours : integer range 0 to 23;
end record time_stamp;

variable sample_time, current_time : time_stamp;

sample_time := current_time;

sample_hour := sample_time.hours;
page 139:
constant midday : time_stamp := (0, 0, 12);
constant midday : time_stamp := (hours => 12, minutes => 0, seconds => 0);

· Chapter 05: Basic Modeling Constructs

page 156:
S'delayed(T)   A signal that takes on the same values as S but is delayed 
               by time T.

S'stable(T)    A Boolean signal that is true if there has been no event on S 
               in the time interval T up to the current time, otherwise false.

S'quiet(T)     A Boolean signal that is true if there has been no transaction 
               on S in the time interval T up to the current time, otherwise 
               false.

S'transaction  A signal of type bit that changes value from ‘0’ to ‘1’ or vice
               versa each time there is a transaction on S.

S'event        True if there is an event on S in the current simulation cycle,
               false otherwise.

S'active       True if there is a transaction on S in the current simulation
               cycle, false otherwise.

S'last_event   The time interval since the last event on S.

S'last_active  The time interval since the last transaction on S.

S'last_value   The value of S just before the last event on S.
page 172:
next_state_logic : process (all) is
begin
  ...
end process next_state;
page 173:
PC_incr : next_PC <= PC + 4 after 5 ns;
PC_incr : process is
begin
  next_PC <= PC + 4 after 5 ns;
  wait on PC;
end process PC_incr;
page 176:
reset_gen : reset <= '1', '0' after 200 ns when extended_reset else
                     '1', '0' after 50 ns;
reset_gen : process is
begin
  if extended_reset then
    reset <= '1', '0' after 200 ns;
  else
    reset <= '1', '0' after 50 ns;
  end if;
  wait;
end process reset_gen;
page 179:
entity full_adder is
port (a, b, c_in : bit; 
      s, c_out : out bit );
end entity full_adder;

architecture truth_table of full_adder is
begin
  with bit_vector'(a, b, c_in) select
(c_out, s) <= bit_vector'("00") when "000",
              bit_vector'("01") when "001",
              bit_vector'("01") when "010",
              bit_vector'("10") when "011",
              bit_vector'("01") when "100",
              bit_vector'("10") when "101",
              bit_vector'("10") when "110",
              bit_vector'("11") when "111";
end architecture truth_table;
page 181:
check : assert not (s and r)
          report "Incorrect use of S_R_flip_flop: " & "s and r both '1'";
check : process is
begin
  assert not (s and r)
    report "Incorrect use of S_R_flip_flop: " & "s and r both '1'";
  wait on s, r;
end process check;
page 182:
entity S_R_flipflop is
port (s, r : in bit;
      q, q_n : out bit );
begin

  check : assert not (s and r)
            report "Incorrect use of S_R_flip_flop: s and r both '1'";
end entity S_R_flipflop;
entity ROM is
port (address : in natural;
      data : out bit_vector(0 to 7);
      enable : in bit );
begin
  trace_reads : process (enable) is
  begin
    if enable then
      report "ROM read at time " & to_string(now) 
                                 & " from address "
                                 & to_string(address);
    end if;
  end process trace_reads;
end entity ROM;

· Chapter 06: Subprograms

page 213:
page 214:
procedure average_samples is
  variable total : real := 0.0;
begin
  assert samples'length > 0 severity failure;
  for index in samples'range loop
    total := total + samples(index);
  end loop;
  average := total / real(samples'length);
end procedure average_samples;
page 234:
page 235:
function limit ( value, min, max : integer ) return integer is
begin
  if value > max then
    return max;
  elsif value < min then
    return min;
  else
    return value;
  end if;
end function limit;
new_temperature := limit (current_temperature + increment, 10, 100);
page 236:
page 237:
network_driver : process is
  constant seq_modulo : natural := 2**5;
  subtype seq_number is natural range 0 to seq_modulo-1;
  variable next_seq_number : seq_number := 0;
  ...
  impure function generate_seq_number return seq_number is
    variable number : seq_number;
  begin
    number := next_seq_number;
    next_seq_number := (next_seq_number + 1) mod seq_modulo;
    return number;
  end function generate_seq_number;
begin -- network_driver
  ...
  new_header := pkt_header'( dest => target_host_id,
  src => my_host_id,
  pkt_type => control_pkt,
  seq => generate_seq_number );
  ...
end process network_driver;

· Chapter 07: Packages and Use Clauses

· Chapter 08: Resolved Signals

page 282:

· Chapter 09: Predefined and Standard Packages

page 302:
page 304:
page 305:
page 311:
page 312:
signal inc_en : std_ulogic;
signal inc_reg : unsigned(7 downto 0);
...
inc_reg_proc : process (clk) is
begin
  if rising_edge(clk) then
    inc_reg <= inc_reg + inc_en;
  end if;
end process inc_reg_proc;
if inc_en = '1' then
  inc_reg <= inc_reg + 1;
end if;
page 314:
page 319:
signal A : ufixed(3 downto -3) := "0110100";
page 320:
signal A4_2 : ufixed(3 downto -2);
signal B3_3 : ufixed(2 downto -3);
signal Y5_3 : ufixed(4 downto -3);
...
Y5_3 <= A4_2 + B3_3;
signal A4 : ufixed(3 downto -3);
...
A4 <= "0110100"; -- string literal for 6.5
A4 <= to_ufixed(6.5, 3, -3); -- pass indices
A4 <= to_ufixed(6.5, A4); -- sized by A4
page 327:
page 328:
page 330:

Conversion Function Summary Charts

· Chapter 10: Pipelined Multiplier Accumulator

page 350:

· Chapter 11: Aliases

page 360:
library ieee; use ieee.std_logic_1164.all;
use work.alu_types.all, work.io_types.all;

architecture structural of controller_system is
  alias alu_data_width is work.alu_types.data_width;
  alias io_data_width is work.io_types.data_width;
  signal alu_in1, alu_in2, alu_result: std_ulogic_vector(0 to alu_data_width - 1);
  signal io_data: std_ulogic_vector(0 to io_data_width - 1);
  ...
begin
  ...
end architecture structural;
page 361:
type register_array is array (0 to 15) of bit_vector(31 downto 0);
type register_set is record
     general_purpose_registers : register_array;
     program_counter : bit_vector(31 downto 0);
     program_status : bit_vector(31 downto 0);
end record;
variable CPU_registers : register_set;

-- we can declare aliases for the record elements:

alias PSW is CPU_registers.program_status;
alias PC is CPU_registers.program_counter;
alias GPR is CPU_registers.general_purpose_registers;

-- We can also declare aliases for individual registers in the register array, for example:

alias SP is CPU_registers.general_purpose_registers(15);

-- The name that we are aliasing can itself be an alias. Hence the alias
-- declaration for SP can be written using the alias name GPR:

alias SP is GPR(15);

-- An alias can also be used to denote a slice of a one-dimensional array. For
-- example, given the above declaration for CPU_registers, we can declare an alias
-- for part of the pro- gram status register:

alias interrupt_level is PSW(30 downto 26);
alias interrupt_level : bit_vector(4 downto 0) is PSW(30 downto 26);

– then interrupt_level(4) would denote PSW(30), interrupt_level(3) would denote PSW(29), and so on.

page 364:
page 365:
type system_status is (idle, active, overloaded);
-- and a model defines an alias for this type:
alias status_type is work.system_types.system_status;

· Chapter 12: Generics

page 374:
entity reg is
  generic (width : positive );
  port (d : in bit_vector(0 to width - 1);
        q : out bit_vector(0 to width - 1);
        clk, reset : in bit );
end entity reg;

word_reg : entity work.reg(behavioral)
  generic map ( width => 32 )
  port map ( ... );
page 377:
entity generic_mux2 is
  generic (type data_type );
  port(sel: in bit;
  a, b: in data_type;
  z : out data_type);
end entity generic_mux2;

architecture rtl of mux2 is
begin
  z <= a when not sel else b;
end architecture rtl;
signal sel_bit, a_bit, b_bit, z_bit : bit;
...
bit_mux : entity work.generic_mux2(rtl)
  generic map (data_type => bit )
  port map(sel => sel_bit,
           a => a_bit,
           b => b_bit,
           z => z_bit);
type msg_packet is record
  src, dst : unsigned(7 downto 0);
  pkt_type : bit_vector(2 downto 0);
  length : unsigned(4 downto 0);
  payload : byte_vector(0 to 31);
  checksum : unsigned(7 downto 0);
end record msg_packet;

signal pkt_sel : bit;
signal pkt_in1, pkt_in2, pkt_out : msg_pkt;
...
pkt_mux : entity work.generic_mux2(rtl)
  generic map ( data_type => msg_packet )
  port map(sel => pkt_sel,
           a => pkt_in1,
           b => pkt_in2,
           z => pkt_out );
page 390:
package bounded_buffer_adt is
  generic ( size : positive;
            type element_type );
  type store_array is array (0 to size - 1) of element_type;

  type bounded_buffer is record
    count : natural;
    head, tail : natural;
    store : store_array;
  end record bounded_buffer;

  procedure reset ( b : inout bounded_buffer );
  -- resets a bounded buffer to be empty

  function is_empty ( b : bounded_buffer ) return boolean;
  -- tests whether the bounded buffer is empty
  -- (i.e., no data to read)

  function is_full ( b : bounded_buffer ) return boolean;
  -- tests whether the bounded buffer is full
  --(i.e., no data can be written)

  procedure write ( b : inout bounded_buffer;
                    data : in
                    element_type );
  -- if the bounded buffer is not full, writes the data
  -- if it is full, assertion violation with severity failure

  procedure read ( b : inout bounded_buffer;
                  data : out element_type );
  -- if the bounded buffer is not empty, read the first item
  -- if it is empty, assertion violation with severity failure
end package bounded_buffer_adt;
page 391:
receiver : process is
  subtype byte is bit_vector(0 to 7);
  package byte_buffer_adt is new work.bounded_buffer_adt
  generic map ( size => 2048, element_type => byte );
  use work.byte_buffer_adt.all;
  variable receive_buffer : bounded_buffer;
  ...

begin
  reset(receive_buffer);
  ...
  if is_full(receive_buffer) then
    ... -- buffer overrun
  else
    write(receive_buffer, received_byte);
  end if;
  ...
  if is_empty(receive_buffer) then
    ... -- buffer underrun
  else
    read(receive_buffer, check_byte);
  end if;
  ...
end process receiver;
page 392:
package body bounded_buffer_adt is
  procedure reset ( b : inout bounded_buffer ) is
  begin
    b.count := 0; b.head := 0; b.tail := 0;
  end procedure reset;

  function is_empty ( b : bounded_buffer ) return boolean is
  begin
    return b.count = 0;
  end function is_empty;

  function is_full ( b : bounded_buffer ) return boolean;
  begin
    return b.count = size;
  end function is_full;

  procedure write ( b : inout bounded_buffer;
                    data : in element_type ) is
  begin
    if is_full(b) then
      report "write to full bounded buffer" severity failure;
    else
      b.store(b.tail) := data;
      b.tail := (b.tail + 1) mod size;
      b.count := b.count + 1;
    end if;
  end procedure write;

  procedure read ( b : inout bounded_buffer;
                   data : out element_type ) is
  begin
    if is_empty(b) then
      report "read from empty bounded buffer" severity failure;
    else
      data := b.store(b.head);
      b.head := (b.head + 1) mod size;
      b.count := b.count - 1;
    end if;
  end procedure read;
end package body bounded_buffer_adt;
page 393:
page 395:
procedure swap
  generic ( type T )
  parameter ( a, b : inout T ) is
  variable temp : T;
begin
  temp := a; a := b; b := temp;
end procedure swap;

-- We can now instantiate the procedure to get versions for various types:
procedure int_swap is new swap
  generic map ( T => integer );
procedure vec_swap is new swap
  generic map ( T => bit_vector(0 to 7) );

-- and call them to swap values of variables:
variable a_int, b_int : integer;
variable a_vec, b_vec : bit_vector(0 to 7);
...
int_swap(a_int, b_int);
vec_swap(a_vec, b_vec);
page 399:
entity generic_counter is
  generic (type count_type;
           constant reset_value : count_type;
           function increment (x : count_type) return count_type
          );

  port (clk, reset : in bit;
        data : out count_type );
end entity generic_counter;

-- We can then use the increment function in the architecture:
architecture rtl of generic_counter is
begin
  count : process (clk) is
  begin
    if rising_edge(clk) then
      if reset then
        data <= reset_value;
      else
        data <= increment(data);
      end if;
    end if;
end process count;
end architecture rtl;

-- Having revised the counter in this way, we can instantiate it with various
-- types.  For example, to create a counter for unsigned values, we define a
-- function, add1, to increment using the “ +” operator on unsigned values and
-- provide it as the actual for the increment generic.

use ieee.numeric_std.all;
function add1 ( arg : unsigned ) return unsigned is
begin
  return arg + 1;
end function add1;

signal clk, reset : bit;
signal count_val : unsigned(15 downto 0);
...
counter : entity work.generic_counter(rtl)
  generic map (count_type => unsigned(15 downto 0),
               reset_value => (others => '0'),
               increment => add1 ) -- add1 is the
                                   -- actual function
  port map ( clk => clk, reset => reset, data => count_val );
page 400:
type traffic_light_color is (red, yellow, green);
function next_color ( arg : traffic_light_color )
  return traffic_light_color is
begin
  if arg = traffic_light_color'high then
    return traffic_light_color'low;
  else
    return traffic_light_color'succ(arg);
end if;
end function next_color;

signal east_light : traffic_light_color;
...
east_counter : work.generic_counter(rtl)
  generic map ( count_type => traffic_light_color,
                reset_value => red,
                increment => next_color ) -- next_color is the
                                          -- actual function
  port map ( clk => clk, reset => reset, data => east_light );

· Chapter 13: Components and Configurations

· Chapter 14: Generate Statements

page 460:
library ieee; use ieee.std_logic_1164.all;
entity shift_reg is
  port (phi1, phi2 : in std_ulogic;
        serial_data_in : in std_ulogic;
        parallel_data : out std_ulogic_vector );
end entity shift_reg;

architecture cell_level of shift_reg is
  alias normalized_parallel_data: std_ulogic_vector(0 to parallel_data'length - 1)
    is parallel_data;

  component master_slave_flipflop is
    port (phi1, phi2 : in std_ulogic;
          d : in std_ulogic;
          q : out std_ulogic );
  end component master_slave_flipflop;

begin
  reg_array : for index in normalized_parallel_data'range generate

  begin
    reg: if index = 0 generate
      cell: component master_slave_flipflop
        port map (phi1, phi2,
                  d => serial_data_in,
                  q => normalized_parallel_data(index) );
    else generate
      cell: component master_slave_flipflop
        port map (phi1, phi2,
                  d => normalized_parallel_data(index - 1),
                  q => normalized_parallel_data(index) );
    end generate other_cell;
  end generate reg_array;
end architecture cell_level;

· Chapter 15: Access Types

page 483:
page 484:
page 485:
count := new natural;
count.all := 10;

-- and we use its value in an expression:

if count.all = 0 then
  ...
end if;
count := new natural;
count.all := 10;

-- we could achieve the same effect with this second form of allocator:
count := new natural'(10);
type stimulus_record is record
  stimulus_time : time;
  stimulus_value : bit_vector(0 to 3);
end record stimulus_record;

type stimulus_ptr is access stimulus_record;

-- and an access variable declared as
variable bus_stimulus : stimulus_ptr;

-- we could create a new stimulus record data object and set bus_stimulus to
point to it as follows:
bus_stimulus := new stimulus_record'( 20 ns, B"0011" );
page 490:
page 495:

· Chapter 16: Files and Input/Output

page 503:
page 506:
architecture behavioral of ROM is
begin
  behavior : process is
    subtype word is std_ulogic_vector(0 to data'length - 1);
    type storage_array is array (natural range 0 to 2**address'length - 1) of word;
    variable storage : storage_array;
    variable index : natural;
    ... -- other declarations
    type load_file_type is file of word;
    file load_file : load_file_type
      open read_mode is load_file_name;
  begin
    -- load ROM contents from load_file
    index := 0;
    while not endfile(load_file) loop
      read(load_file, storage(index));
      index := index + 1;
    end loop;
    -- respond to ROM accesses
    loop
    ...
    end loop;
  end process behavior;
end architecture behavioral;
page 512:
page 518:
page 521:
page 522:

· Chapter 17: Case Study: A Package for Memories

· Chapter 18: Test Bench and Verification Features

page 567:
library ieee; use ieee.std_logic_1164.all;
entity control is
  port ( clk, reset : in std_ulogic; ... );
end entity control;

architecture fsm of control is
  subtype state_type is std_ulogic_vector(3 downto 0);
  constant idle : state_type := "0000";
  constant pending1 : state_type := "0001";
  ...
  signal current_state, next_state : state_type;
begin
  state_reg : process (clk) is ...
  fsm_logic : process (all) is ...
end architecture fsm;

--The entity and architecture for the system being designed are

library IEEE; use IEEE.std_logic_1164.all;
entity system is
  port ( clk, reset : in std_ulogic; ... );
end entity system;

architecture rtl of system is
  component control is
    port ( clk, reset : in std_ulogic; ... );
  end component control;
begin
  control_unit : component control
    port map ( clk => clk, reset => reset, ... );
    ...
end architecture rtl;

--We can define a test bench entity and architecture that traces the sequence of
--states in the control unit, issuing a report message for each:

entity state_monitor is
end entity state_monitor;

architecture tracing of state_monitor is
  alias fsm_clk is
    <<signal .tb.system_duv.control_unit.clk : std_ulogic>>;
  alias fsm_state is
    <<signal .tb.system_duv.control_unit.current_state : std_ulogic_vector(3 downto 0)>>;

begin
  monitor : process (fsm_clk) is
  begin
    if falling_edge(fsm_clk) then
      report to_string(now) & ": " & to_string(fsm_state);
    end if;
  end process monitor;
end architecture tracing;

--we write the top-level entity and architecture as

library IEEE; use IEEE.std_logic_1164.all;
entity tb is
end entity tb;

architecture monitoring of tb is
  signal system_clk, system_reset : std_ulogic;
  ...
	begin
    ... -- clock and reset generation
    system_duv : entity work.system(rtl)
      port map ( clk => system_clk, reset => system_reset, ... );
    state_monitor : entity work.state_monitor(tracing);
end architecture monitoring;

· Chapter 19: Shared Variables and Protected Types

page 587:
page 588:
type shared_counter is protected
  procedure reset;
  procedure increment ( by : integer := 1 );
  impure function value return integer;
end protected shared_counter;

-- This declares a protected type for a shared counter with methods to reset the
-- counter value to zero, to increment the counter by some amount and to read the
-- value of the counter.
-- We can declare a shared variable to be of this type using a shared variable decla-
-- ration, for example:

shared variable event_counter : shared_counter;

-- A process uses the name of the shared variable as a prefix to a method name
-- to identify the shared variable on which the method is invoked. For example:

event_counter.reset;
event_counter.increment (2);
assert event_counter.value > 0;

-- In each case, the process acquires exclusive access to event_counter before
-- executing the body of the method. While the process is executing the method,
-- other processes that try to invoke any method on the same shared variable must
-- wait. 

· Chapter 20: Attributes and Groups

page 606:
page 607:
page 608:
page 609:

· Chapter 21: Design for Synthesis

page 638:
page 639:
page 642:
with addr(1 downto 0) select
  request <= request_a when "00",
             request_b when "01",
             request_c when "10",
             request_d when "11",
             'X' when others;
request <= request_a when addr(1 downto 0) = "00" else
           request_b when addr(1 downto 0) = "01" else
           request_c when addr(1 downto 0) = "10" else
           request_d when addr(1 downto 0) = "11" else
           'X';
page 654:
page 661:
package RTL_ATTRIBUTES is
  attribute KEEP : boolean;
  attribute CREATE_HIERARCHY : boolean;
  attribute DISSOLVE_HIERARCHY : boolean;
  attribute SYNC_SET_RESET : boolean;
  attribute ASYNC_SET_RESET : boolean;
  attribute ONE_HOT : boolean;
  attribute ONE_COLD : boolean;
  attribute FSM_STATE : string;
  attribute FSM_COMPLETE : boolean;
  attribute BUFFERED : string;
  attribute INFER_MUX : boolean;
  attribute IMPLEMENTATION : string;
  attribute RETURN_PORT_NAME : string;
  attribute ENUM_ENCODING : string;
  attribute ROM_BLOCK : string;
  attribute RAM_BLOCK : string;
  attribute LOGIC_BLOCK : string;
  attribute GATED_CLOCK : boolean;
  attribute COMBINATIONAL : boolean;
end package RTL_ATTRIBUTES;
page 668:
-- rtl_synthesis off

-- rtl_synthesis on

· Chapter 22: Case Study: System Design Using the Gumnut Core

page 673:
page 675:
page 676:
page 677:
page 707:
page 711:

· Chapter 23: Miscellaneous Topics