Practice

Synth Cutoff Dial

A music shop sells a build-it-yourself synthesizer kit. One knob sets the filter cutoff: turn it up and the sound gets brighter, turn it down and it goes dull. The panel chip reads the knob and hands this block an 8-bit value. The filter chip wants a smooth control voltage, not a number, so the board converts it with a PWM DAC. The block sends pulses: high for part of each period, low for the rest. An RC filter averages the pulses into a steady voltage, so a bigger value means a wider pulse and a higher voltage.

The first batch of kits had a fault. The old block copied each new knob value into its pulse logic right away, even in the middle of a period. The pulse under way changed width, and the filter voltage jumped instead of gliding. A slow turn of the knob played back as a row of soft clicks. Buyers called it zipper noise, and the kit's reviews turned sour.

Your task: build the PWM DAC block. Generate back-to-back periods of exactly 256 clock cycles each. Sample level_i once per period, at the period boundary, and hold the sampled value for the whole period. Drive pwm_o high for that many cycles at the start of the period, then low until the period ends. A sampled level of 0 keeps pwm_o low for the whole period. A sampled level of 255 is full scale: keep pwm_o high for the whole period.

Interface

PortDirectionTypeDescription
clk_iin1 bitSystem clock
rst_iin1 bitSynchronous reset (active high)
level_iin8-bit vectorRequested output level
pwm_oout1 bitPulse output, to the board's RC filter

Behavior

  • Each period is exactly 256 clock cycles; periods run back to back with no gap
  • Cycle numbering: cycle 0 is the first cycle of a period and cycle 255 is the last
  • The level_i input is sampled at the clock edge between cycle 255 and cycle 0 of the next period
  • The sampled value is fixed for the whole period it starts
  • Changes on level_i in the middle of a period take effect at the next boundary, never inside the running period
  • For a sampled value L from 1 to 254, pwm_o is high during cycles 0 to L-1 and low during cycles L to 255
  • A sampled value of 0 keeps pwm_o low for the whole period
  • A sampled value of 255 is full scale: pwm_o is high for all 256 cycles of the period
  • With this rule a duty of 255 out of 256 cannot occur; full scale buys a true always-high output instead
  • rst_i high at a rising clock edge restarts the period and clears the sampled value to 0
  • The first cycle after the last edge with rst_i high is cycle 0 of a fresh period
  • The first period after reset runs with a sampled value of 0: pwm_o stays low for that whole period

What the bench checks

  • The testbench checks pwm_o on every cycle of every period against the value sampled at that period's boundary
  • The testbench holds a nonzero level_i through reset and checks the first period after reset stays low
  • The testbench runs full periods at sampled values 0, 1, 64, 128, 165, 192, 254, and 255
  • The testbench changes level_i in the middle of most periods and checks the running period never changes shape
  • The testbench raises level_i mid-period after the pulse has ended and checks no second pulse appears in that period
  • The testbench changes level_i twice inside one period and checks the next period uses the value present at the boundary
  • The testbench asserts rst_i in the middle of a high pulse and checks pwm_o is low on the next cycle
  • The testbench asserts rst_i on the exact edge where a period wraps, holds a nonzero level_i, and checks the next period stays low
  • The testbench counts cycles without a break across periods, so a period of 255 or 257 cycles fails

Constraints

TIMING: everything is synchronous to clk_i; pwm_o changes only at clock edges.

SCOPE: this models the digital PWM generator only. The panel chip's knob reading, the RC filter, and the filter chip's analog side are out of scope.

Do not add ports.

Loading editor...

Click Run to execute your code. Output will appear here.