SvPortSim.Protocol.DataType (SvPortSim v0.1.0)

Copy Markdown View Source

Defines the MVP SystemVerilog data-type subset and runtime value encoding.

This module is the single source of truth for the value shapes that the runtime protocol accepts in the first release. It is intentionally narrower than SystemVerilog: only scalar bit and logic, one-dimensional packed vectors, explicit signed and unsigned integer views, and clock/reset roles are supported.

Runtime representation

Every runtime value is encoded as a map with two fields:

  • :bits - a string containing one character per bit
  • :width - the encoded bit width

Bit strings are ordered from the most significant bit to the least significant bit. For the canonical packed vector range [width - 1:0], index width - 1 is the first character and index 0 is the last character.

Two-state values may only contain "0" and "1". Four-state values may also contain "x" for unknown and "z" for high impedance. "x" and "z" are preserved for scalar and vector values, but they are never accepted for integer values because integers are numeric views over two-state bits.

Supported MVP subset

MVP typeSystemVerilog shapeRuntime value
:bitbitone two-state bit
:logiclogicone four-state bit
{:bit_vector, width}bit [width - 1:0]two-state bit string
{:logic_vector, width}logic [width - 1:0]four-state bit string
{:uint, width}unsigned numeric viewElixir non-negative integer
{:int, width}signed numeric viewElixir integer using two's complement
:clockscalar bit roleone two-state bit
:resetscalar bit roleone two-state bit with active level

Vector widths are constrained to 1..4096 bits. Packed vector ranges are canonicalised to [width - 1:0]; non-canonical source ranges can be represented only after conversion to this runtime width and ordering.

Examples

iex> SvPortSim.Protocol.DataType.max_vector_width()
4096

iex> SvPortSim.Protocol.DataType.supported?(:bit)
true

iex> SvPortSim.Protocol.DataType.supported?({:logic_vector, 8})
true

iex> SvPortSim.Protocol.DataType.supported?({:logic_vector, 0})
false

iex> SvPortSim.Protocol.DataType.encode({:logic_vector, 4}, "10XZ")
{:ok, %{bits: "10xz", width: 4}}

iex> SvPortSim.Protocol.DataType.encode({:bit_vector, 4}, "10xz")
{:error, {:invalid_bits, "10xz", ["0", "1"]}}

iex> {:ok, type} = SvPortSim.Protocol.DataType.signed_integer(8)
iex> SvPortSim.Protocol.DataType.encode(type, -1)
{:ok, %{bits: "11111111", width: 8}}
iex> SvPortSim.Protocol.DataType.decode(type, %{bits: "11111110"})
{:ok, -2}

iex> {:ok, clock} = SvPortSim.Protocol.DataType.clock()
iex> clock.role
:clock
iex> SvPortSim.Protocol.DataType.encode(clock, true)
{:ok, %{bits: "1", width: 1}}

Summary

Types

A supported SystemVerilog base type.

A protocol value encoded as a bit string plus its width.

A supported runtime type category.

A decoded native Elixir value.

A supported signal role.

The accepted state space for encoded bits.

t()

A normalized MVP data-type descriptor.

Values accepted by normalize/1 as type descriptors.

Functions

Returns a normalized descriptor for scalar bit.

Returns a normalized descriptor for a packed bit vector.

Returns a normalized descriptor for a clock signal role.

Decodes a runtime bit-string value into a native Elixir value.

Decodes an encoded runtime value and raises ArgumentError on failure.

Encodes a native Elixir value as a runtime bit-string value.

Encodes a value and raises ArgumentError on failure.

Returns a normalized descriptor for an integer view.

Returns a normalized descriptor for scalar logic.

Returns a normalized descriptor for a packed logic vector.

Returns the maximum packed-vector width supported by the MVP runtime contract.

Normalises a type descriptor or shorthand into the canonical map form.

Returns a normalized descriptor for a reset signal role.

Returns a normalized scalar descriptor for base.

Returns a normalized descriptor for a signed integer view.

Returns whether a type descriptor is supported by the MVP runtime contract.

Returns an explicit table of the data types supported by the MVP.

Returns a normalized descriptor for an unsigned integer view.

Returns the explicitly unsupported SystemVerilog type forms for the MVP.

Returns a normalized packed-vector descriptor.

Types

base()

@type base() :: :bit | :logic

A supported SystemVerilog base type.

encoded_value()

@type encoded_value() :: %{bits: String.t(), width: pos_integer()}

A protocol value encoded as a bit string plus its width.

kind()

@type kind() :: :scalar | :vector | :integer

A supported runtime type category.

native_value()

@type native_value() :: integer() | :x | :z | String.t()

A decoded native Elixir value.

role()

@type role() :: :data | :clock | :reset

A supported signal role.

states()

@type states() :: :two | :four

The accepted state space for encoded bits.

t()

@type t() :: %{
  :base => base(),
  :kind => kind(),
  :role => role(),
  :signed => boolean(),
  :states => states(),
  :width => pos_integer(),
  optional(:active) => :high | :low
}

A normalized MVP data-type descriptor.

typeish()

@type typeish() ::
  t()
  | :bit
  | :logic
  | :clock
  | :reset
  | {:clock, base()}
  | {:reset, :high | :low}
  | {:reset, :high | :low, base()}
  | {:bit_vector, pos_integer()}
  | {:bit_vector, pos_integer(), boolean() | :signed | :unsigned}
  | {:logic_vector, pos_integer()}
  | {:logic_vector, pos_integer(), boolean() | :signed | :unsigned}
  | {:uint, pos_integer()}
  | {:int, pos_integer()}
  | {:integer, pos_integer(), boolean() | :signed | :unsigned}

Values accepted by normalize/1 as type descriptors.

Functions

bit()

@spec bit() :: t()

Returns a normalized descriptor for scalar bit.

Examples

iex> SvPortSim.Protocol.DataType.bit().states
:two

bit_vector(width, opts \\ [])

@spec bit_vector(
  term(),
  keyword()
) :: {:ok, t()} | {:error, term()}

Returns a normalized descriptor for a packed bit vector.

Examples

iex> {:ok, type} = SvPortSim.Protocol.DataType.bit_vector(4)
iex> type.width
4

clock(base \\ :bit)

@spec clock(term()) :: {:ok, t()} | {:error, term()}

Returns a normalized descriptor for a clock signal role.

A clock is a scalar bit or logic value with the :clock role. Runtime clock values are always driven as 0 or 1; x and z are rejected even when the base type is logic.

Examples

iex> {:ok, clock} = SvPortSim.Protocol.DataType.clock(:logic)
iex> {clock.role, clock.states}
{:clock, :four}

decode(type, encoded)

@spec decode(typeish() | term(), encoded_value() | map() | String.t()) ::
  {:ok, native_value()} | {:error, term()}

Decodes a runtime bit-string value into a native Elixir value.

Scalar values decode to 0, 1, :x, or :z. Vector values decode to a normalized bit string. Integer values decode to Elixir integers.

Examples

iex> SvPortSim.Protocol.DataType.decode(:logic, %{bits: "Z"})
{:ok, :z}

iex> SvPortSim.Protocol.DataType.decode({:logic_vector, 4}, %{bits: "10XZ"})
{:ok, "10xz"}

iex> SvPortSim.Protocol.DataType.decode({:int, 4}, %{bits: "1111"})
{:ok, -1}

decode!(type, encoded)

@spec decode!(typeish() | term(), encoded_value() | map() | String.t()) ::
  native_value()

Decodes an encoded runtime value and raises ArgumentError on failure.

Examples

iex> SvPortSim.Protocol.DataType.decode!({:uint, 4}, %{bits: "1010"})
10

encode(type, value)

@spec encode(typeish() | term(), term()) :: {:ok, encoded_value()} | {:error, term()}

Encodes a native Elixir value as a runtime bit-string value.

Scalar and vector values accept bit strings, bit lists, integers 0 and 1, booleans, :x, and :z as appropriate for the type state space. Integer views accept Elixir integers and encode them as unsigned or two's complement bits.

Examples

iex> SvPortSim.Protocol.DataType.encode(:bit, 1)
{:ok, %{bits: "1", width: 1}}

iex> SvPortSim.Protocol.DataType.encode({:logic_vector, 4}, [1, 0, :x, :z])
{:ok, %{bits: "10xz", width: 4}}

iex> SvPortSim.Protocol.DataType.encode({:uint, 4}, 16)
{:error, {:integer_out_of_range, 16, {0, 15}}}

encode!(type, value)

@spec encode!(typeish() | term(), term()) :: encoded_value()

Encodes a value and raises ArgumentError on failure.

Examples

iex> SvPortSim.Protocol.DataType.encode!(:bit, false)
%{bits: "0", width: 1}

integer(width, opts \\ [])

@spec integer(
  term(),
  keyword()
) :: {:ok, t()} | {:error, term()}

Returns a normalized descriptor for an integer view.

Integer views are always two-state. Unknown and high-impedance values are not valid integer values in the MVP protocol.

Examples

iex> {:ok, type} = SvPortSim.Protocol.DataType.integer(4, signed: :unsigned)
iex> {type.kind, type.width, type.signed, type.states}
{:integer, 4, false, :two}

logic()

@spec logic() :: t()

Returns a normalized descriptor for scalar logic.

Examples

iex> SvPortSim.Protocol.DataType.logic().states
:four

logic_vector(width, opts \\ [])

@spec logic_vector(
  term(),
  keyword()
) :: {:ok, t()} | {:error, term()}

Returns a normalized descriptor for a packed logic vector.

Examples

iex> {:ok, type} = SvPortSim.Protocol.DataType.logic_vector(4, signed: :signed)
iex> {type.states, type.signed}
{:four, true}

max_vector_width()

@spec max_vector_width() :: pos_integer()

Returns the maximum packed-vector width supported by the MVP runtime contract.

Examples

iex> SvPortSim.Protocol.DataType.max_vector_width()
4096

normalize(descriptor)

@spec normalize(typeish() | term()) :: {:ok, t()} | {:error, term()}

Normalises a type descriptor or shorthand into the canonical map form.

Examples

iex> {:ok, type} = SvPortSim.Protocol.DataType.normalize(:bit)
iex> {type.base, type.width, type.states}
{:bit, 1, :two}

iex> {:ok, type} = SvPortSim.Protocol.DataType.normalize({:logic_vector, 8, :signed})
iex> {type.base, type.width, type.signed}
{:logic, 8, true}

reset(opts \\ [])

@spec reset(keyword()) :: {:ok, t()} | {:error, term()}

Returns a normalized descriptor for a reset signal role.

Options:

  • :base - :bit or :logic. Defaults to :bit.
  • :active - :high or :low. Defaults to :high.

Runtime reset values are always driven as 0 or 1; x and z are rejected even when the base type is logic.

Examples

iex> {:ok, reset} = SvPortSim.Protocol.DataType.reset(active: :low)
iex> {reset.role, reset.active}
{:reset, :low}

scalar(base)

@spec scalar(term()) :: {:ok, t()} | {:error, term()}

Returns a normalized scalar descriptor for base.

Examples

iex> {:ok, type} = SvPortSim.Protocol.DataType.scalar(:bit)
iex> {type.base, type.width, type.states}
{:bit, 1, :two}

iex> SvPortSim.Protocol.DataType.scalar(:byte)
{:error, {:invalid_base, :byte}}

signed_integer(width)

@spec signed_integer(term()) :: {:ok, t()} | {:error, term()}

Returns a normalized descriptor for a signed integer view.

Signed integers use two's complement bit encoding.

Examples

iex> {:ok, type} = SvPortSim.Protocol.DataType.signed_integer(4)
iex> SvPortSim.Protocol.DataType.encode(type, -8)
{:ok, %{bits: "1000", width: 4}}

supported?(type)

@spec supported?(term()) :: boolean()

Returns whether a type descriptor is supported by the MVP runtime contract.

Examples

iex> SvPortSim.Protocol.DataType.supported?({:uint, 32})
true

iex> SvPortSim.Protocol.DataType.supported?({:real, 64})
false

supported_types()

@spec supported_types() :: [map()]

Returns an explicit table of the data types supported by the MVP.

The table is data, not prose, so tests and downstream modules can assert that the supported subset has not changed accidentally.

Examples

iex> SvPortSim.Protocol.DataType.supported_types() |> Enum.map(& &1.name)
[:bit, :logic, :bit_vector, :logic_vector, :unsigned_integer, :signed_integer, :clock, :reset]

unsigned_integer(width)

@spec unsigned_integer(term()) :: {:ok, t()} | {:error, term()}

Returns a normalized descriptor for an unsigned integer view.

Examples

iex> {:ok, type} = SvPortSim.Protocol.DataType.unsigned_integer(4)
iex> SvPortSim.Protocol.DataType.encode(type, 15)
{:ok, %{bits: "1111", width: 4}}

unsupported_features()

@spec unsupported_features() :: [atom()]

Returns the explicitly unsupported SystemVerilog type forms for the MVP.

This list is intentionally conservative. A type form should be removed from this list only when normalize/1, encode/2, and decode/2 define exact behaviour for it.

Examples

iex> :unpacked_arrays in SvPortSim.Protocol.DataType.unsupported_features()
true

vector(base, width, opts \\ [])

@spec vector(term(), term(), keyword()) :: {:ok, t()} | {:error, term()}

Returns a normalized packed-vector descriptor.

:signed may be true, false, :signed, or :unsigned.

Examples

iex> {:ok, type} = SvPortSim.Protocol.DataType.vector(:logic, 2, signed: true)
iex> {type.base, type.width, type.signed, type.states}
{:logic, 2, true, :four}