name: "SPI Mode-0 Master"
category: "Embedded Control & I/O"
complexity: "Medium"
description: |
  Implement a synchronous SPI master that performs one full-duplex
  8-bit transfer at a time.

  PROTOCOL SUBSET:
  - SPI mode 0 only: `sclk` idles low, data changes while `sclk` is low,
    and input data is sampled on each rising edge of `sclk`.
  - Each accepted transaction transfers exactly 8 bits.
  - Bits are transferred most-significant bit first.

  START AND TIMING:
  - A transfer request is accepted on a rising edge of `clk` when
    `start` is high and `busy` is low.
  - Immediately after an accepted request:
      - `busy` must be high
      - `cs_n` must be low
      - `sclk` must remain low
      - `mosi` must present `tx_data[7]`, the first bit to transmit
  - While a transfer is active, `sclk` uses fixed timing:
      - on each subsequent rising edge of `clk`, `sclk` toggles
      - therefore the first toggle after an accepted request is a rising
        edge of `sclk`
      - one full `sclk` period is exactly 2 cycles of `clk`
  - On each rising edge of `sclk`, sample the current value of `miso`.
    The first rising edge samples the most-significant received bit.
  - On each falling edge of `sclk`, if more bits remain to be sent, update
    `mosi` to the next transmit bit while `sclk` is low.

  COMPLETION:
  - After the 8th rising edge of `sclk`, the received byte is complete.
  - On the next rising edge of `clk` after that final sample:
      - `busy` must go low
      - `done` must pulse high for exactly one cycle
      - `cs_n` must return high
      - `sclk` must return low
      - `rx_data` must hold the assembled received byte
  - `rx_data` is only required to be valid in cycles where `done` is high.

  BUSY BEHAVIOUR:
  - While `busy` is high, additional assertions of `start` must be ignored.
  - While idle, `done` must be low, `cs_n` must be high, and `sclk` must
    be low.

  RESET:
  - `rst` is synchronous and active-high.
  - While `rst` is high, the module must be idle: `busy=0`, `done=0`,
    `cs_n=1`, and `sclk=0`.
  - If `rst` is asserted during a transfer, the transfer is aborted and
    the module returns to the idle state immediately after that rising
    edge of `clk`.

interface:
  module_name: "spi_master_controller"
  clocked: true
  ports:
    - name: "clk"
      type: "input"
      width: "1"
      description: "System clock"
    - name: "rst"
      type: "input"
      width: "1"
      description: "Synchronous active-high reset"
    - name: "start"
      type: "input"
      width: "1"
      description: "Pulse high to request a new 8-bit transfer when idle"
    - name: "tx_data"
      type: "input"
      width: "8"
      description: "Transmit byte, sent MSB first"
    - name: "miso"
      type: "input"
      width: "1"
      description: "SPI input sampled on rising edges of sclk"
    - name: "mosi"
      type: "output"
      width: "1"
      description: "SPI output driven MSB first"
    - name: "sclk"
      type: "output"
      width: "1"
      description: "SPI serial clock in mode 0"
    - name: "cs_n"
      type: "output"
      width: "1"
      description: "Active-low chip select"
    - name: "busy"
      type: "output"
      width: "1"
      description: "High while a transfer is in progress"
    - name: "done"
      type: "output"
      width: "1"
      description: "One-cycle pulse when rx_data is valid"
    - name: "rx_data"
      type: "output"
      width: "8"
      description: "Received byte assembled from sampled miso bits"

target_platform:
  device_family: "AMD Xilinx Zynq-7000"

tasks:
  - name: "Area-Efficient SPI Master"
    constraints:
      max_lut: 90
      max_ff: 64
      max_bram: 0
      max_dsp: 0
      min_frequency: 180
    objective:
      goal: "minimise"
      target: "LUT"
