name: "Streaming FIX Message Decoder"
category: "Networking & Communications"
complexity: "Medium"
description: >
  Implement a streaming parser for ASCII tag-value messages used by a
  financial market gateway.

  Each message is a sequence of fields separated by the SOH delimiter
  (8'h01). Every field has the format TAG=VALUE<SOH>, where TAG is a
  decimal ASCII integer and VALUE is an ASCII string.

  The module receives one byte per clock cycle and must report the
  following information for each complete message:

    Tag 35 (Message Type): A single ASCII character. Drive the raw
      8-bit ASCII code on `msg_type`.

    Tag 49 (Sender Identifier): Up to 8 ASCII characters. Drive the
      value on `sender_id` with the first character in the most-
      significant byte and any unused bytes set to zero.

      Examples:
      - VALUE = "AB" must produce `sender_id = 64'h4142000000000000`
      - VALUE = "ABCDEFGH" must produce `sender_id = 64'h4142434445464748`
      - VALUE = "A" must produce `sender_id = 64'h4100000000000000`

    Tag 34 (Sequence Number): ASCII decimal digits. Convert the value to
      a 32-bit unsigned binary number on `msg_seq_num`.

  MESSAGE BOUNDARY:
    Tag 10 always appears exactly once and is always the final field in
    the message. Its value is a 3-digit ASCII decimal string. You do not
    need to validate Tag 10 and you do not need to compute any checksum;
    use the SOH byte terminating Tag 10 as the end-of-message marker.

  INTERFACE BEHAVIOUR:
  - `data_in` presents one new byte on every rising edge of `clk`.
  - When the SOH byte terminating Tag 10 is received, pulse `valid` high
    for exactly one cycle. On that cycle, `msg_type`, `sender_id`, and
    `msg_seq_num` must correspond to the completed message.
  - After that pulse, the parser must be ready to process the next
    message immediately on the following cycle.
  - Tags other than 35, 49, 34, and 10 may appear and must be ignored.
  - Tags 35, 49, and 34 may appear in any order before Tag 10.
  - Each message contains exactly one instance of tags 35, 49, 34, and
    10.
  - `rst` is synchronous and active-high and clears all internal state.

  ADDITIONAL CLARIFICATIONS:
  - Parse the decimal ASCII tag number until `=` is seen, then parse the
    value bytes until SOH is seen.
  - When starting the value for tag 49, clear the temporary sender
    register for the current message and store bytes MSB-first.
    A simple correct implementation is:
    first character -> bits [63:56], second -> [55:48], ...,
    eighth -> [7:0].
  - When starting the value for tag 34, clear the temporary sequence
    accumulator for the current message, then update it as
    `value = value * 10 + digit`.
  - It is recommended to use temporary accumulators for the current
    message and only copy them to the outputs when Tag 10 completes.
  - `valid` must be high only on the cycle that receives the final SOH
    of Tag 10. On all other cycles `valid` must be low.
  - After that `valid` pulse, clear the temporary accumulators so the
    next message starts fresh on the next cycle.

  EXAMPLE MESSAGE:
  - Input byte stream:
    `8=FIX.4.2<SOH>35=8<SOH>49=AB<SOH>34=42<SOH>10=005<SOH>`
  - On the cycle that receives the final SOH after `10=005`,
    drive:
    `valid = 1`, `msg_type = 8'h38`,
    `sender_id = 64'h4142000000000000`,
    `msg_seq_num = 32'd42`

interface:
  module_name: "fix_msg_decoder"
  clocked: true
  ports:
    - name: "clk"
      type: "input"
      width: "1"
      description: "Clock signal"
    - name: "rst"
      type: "input"
      width: "1"
      description: "Synchronous reset, active high"
    - name: "data_in"
      type: "input"
      width: "8"
      description: "Input byte from message stream"
    - name: "valid"
      type: "output"
      width: "1"
      description: "High when a complete message has been parsed"
    - name: "msg_type"
      type: "output"
      width: "8"
      description: "Extracted MsgType (Tag 35) as raw ASCII byte"
    - name: "sender_id"
      type: "output"
      width: "64"
      description: "Extracted SenderCompID (Tag 49), MSB-first ASCII, unused bytes zero"
    - name: "msg_seq_num"
      type: "output"
      width: "32"
      description: "Extracted MsgSeqNum (Tag 34) as 32-bit unsigned integer"

target_platform:
  device_family: "AMD Xilinx Zynq-7000"

tasks:
  - name: "Low-Latency Market Message Parser"
    constraints:
      min_frequency: 200
    objective:
      goal: "maximise"
      target: "Fmax"
