General Description of circuit 'pet' to be converted to Confluence: Ganesh Gopalakrishnan, School of Computing, University of Utah ganesh@cs.utah.edu ============================================================== Background: ----------- A circuit called 'pet' (Programmable Electronic Tune generator) is described. In a nutshell, this is a finite-state machine (FSM) controlled frequency divider, where the FSMs are responsible for accepting and storing the sequence of divider values to be used, and sequencing a frequency divider through that sequence. The frequency divider output (a frequency-modulated square-wave) is sent directly to a speaker. It plays a "song" with the same tonal qualities as an inexpensive electronic keyboard. This circuit was recently synthesized and tested on the ISE XST-50 board, which is a board full of interesting peripheral units and connectors (www.xess.com). On top of XST-50 rides another (smaller) board called "XSA-50" which contains a 50K-gate Spartan-2 FPGA, a sync. DRAM built by Hynix, a Flash memory, and many other goodies. This writeup describes our futher enhancements of this design, and also describes its present form that is in VHDL and uses the Hynix SDRAM module. Luckily for us, a pipelined Sync. DRAM controller has been made available at the Xess company website. We are using this DRAM controller "as is," with only a limited understanding of its internal operation (its external behavior matches the specification on the Xess website). Overview of operation: ---------------------- The heart of 'pet' is 'petcore' which takes several user commands, most of which help define a "music sheet" - i.e. a sequence of frequency values and durations. Petcore also has a 'play' command which then plays the music sheet. This consists of playing the first frequency f0 for its time duration d0, then switching to frequency f1 which is played for d1, etc. For demo purposes I have put an upper bound of 8 notes and durations - and so a 'song' consists of f0,d0 through f7,d7. In fact, for demo purposes, I've dummied the "duration" part inside 'petcore', so all notes will play with a fixed hard-wired duration. I'll include the 'duration' feature in the following narrative as if it is present. (This is because the ability to handle duration-related commands is retained in all other modules than petcore.) The commands supported by 'petcore' are the following. I describe the "upf/upd/next" group first, as these pertain to "going up" - and are all mapped to one particular input switch called "s5" switch, on the XST-50 board. (I'll explain in a minute how one switch can carry out three commands.) Similarly, the "going down" group of commands are mapped on the s4 switch, and these are "dnf/dnd/prev". Lastly, the "action" commands are mapped onto the s3 switch, these being "load/play/test". An LED 7-segment display will show these commands using characters that are largely non-standard, but approximately as follows: (upf as "F", upd as "D", next as a non-standard character, dnf as "f", dnd as "d", and prev as a non-standard character, load as "L", play as "P", and test as "7"). You will see an exact description in module xsa_display. The commands are also explained in some detail shortly. Switch encoding: ---------------- Switches, s5, s4, and s3, are on the XST board, providing a normally high output. When the switch is pressed, it produces a low signal. The switches are *not* de-bounced, and so some care has to be exercised in avoiding multiple samples. Rather than employ a debouncer, I have relied on the human response time to filter out the bounces! Here is how it works. I keep the LED of the 7-segment display continually flashing as a 'heart-beat'. The heart-beat pulse is on for 1/8th of a second and with frequency roughly 1 second. So here is what the user must do for invoking each command: - upf: Watch one flash go by; then immediately press s5; hold s5 down till you see ONE more flash go by; then release s5. - upd: Watch one flash go by; then immediately press s5; hold s5 down till you see TWO more flashes go by; then release s5. - next: Ditto, except after s5 is pressed, wait for THREE or more flashes to go by before releasing s5. - dnf: same as upf, except s4 is involved - dnd: same as upd, except s4 is involved - prev: same as next, except s4 is involved - load: same as upf and upd, except s3 is involved - play: same as dnf and dnd, except s3 is involved - test: same as next and prev, except s3 is involved Thus, by having *our* circuit sample the switch outputs when the user has one of them pressed, the 'bounce' period is being ignored! It also serves as a nice exercise in time-duration semantics that you do see in many real-world device switches. Command details: ---------------- The commands of pet are as follows: - 'upf' for "up frequency": Decrease (not increase) the value of a counter called "fc". Counter "fc" acts as a clock divider. Thus, by dividing the clock with a lower divisor, we get a higher frequency. Also update output to LED display via variable v_cmd, which should now show "F". - 'upd' for "up duration" : Increase the value of "dc" (dummied). LED shows "D". - 'next' for "next" : Increases the pointer "ep" (for entry pointer) that points to the memory location in which the frequency and duration are to be deposited. Note: we do not guard against going beyond 8, the last location from which a note will be played. - 'dnf' for "down frequency" : Same as "upf" except "fc" is incremented instead of being decremented, and the display shows lower-case "f" in an approximate sense. - 'dnd' for "down duration" : Same as "upd" except "dc" is decremented instead of being incremented. The display also crudely shows "d" - 'prev' for "previous" : Same as "next" except "ep" is decremented. Note: we do not guard against rolling down from 0 to 3FFFF, so do not decrement too much. - 'load' for "loading" : Load the current values of "fc" and "dc" in the memory at location "ep". Note: since the "duration" aspects are dummied, we enter only the frequency value "fc" at location ep. - 'play' for "playing" : go play the song (see description below) - 'test' for "testing" : dummied for now - currently shows "7" (crudely, a "t"). How a tune gets played: ----------------------- When the "play" command is invoked, the 'petcore' unit engages another module called 'player' into action. (In reality, we call these modules "petcoresdram," "playersdram," etc., to distinguish them from the modules of an earlier design that did not use an sdram. However we will often use the shortened form 'petcore' and 'player'.) The player module in turn employs the 'durc' (duration counter) module. Here is how things go: Handshakes: ---------- Upon receiving the 'play' command, 'petcore' issues 'go_play' to player and waits for 'play_over'. When play_over is asserted, petcore negates go_play and waits for play_over to be negated (full four-phase handshake, in other words). This is to facilitate independent testing and to keep the design simple and stupid. Similarly, whenever the player wants to measure a certain time duration, it sets a duration input to the duration counter and says 'go_dur". The duration counter engages in a four-phase handshake by asserting "dur_over". Then go_dur is deasserted, followed by durc deasserting dur_over. How a tune is played: --------------------- Upon go_play, jam the first frequency value into the input of tonegen. This gets the first note going. An "alarm" is set by triggering go_dur. When the alarm rings (dur_over), the player goes to the next play location (if within 8 notes in the demo version) and jams that frequency value into the input of tonegen, getting the second note going. The process repeats till the whole song has been played. Clock division and distribution details: ---------------------------------------- The XSA-50 board clock is "xsa_clk." It goes into the xsasdramcntl module. This module generates "clk_b" whose purpose is to generate a "system reset" till the SDRAM has stabilized in operation. After that, "clk_b" plays no role. The clock that matters thereafter is "clk_i". Clock "clk_i" is stable after the delay-lock loops (DLLs) have generated the "lock" signal. This is the "main" clock that goes into "sclkgen". For the purposes of higher level reasoning, treat "clk_i" as THE clock. "sclkgen" generates "sclk" (sometimes called "s1clk") and "s8clk". The former is supposed to have roughly a 1-second period (or roughly 1 Hz). The latter is supposed to have roughly a 1/8-th second period (or roughly 8 Hz). Here is how these clocks are distributed: "sclkgen" also generates the flashing decimal-point. As discussed earlier, this flashes at roughly 1 Hz, with each flash lasting 1/8-th of a second. - sdec (instances s5dec, s4dec, and s3dec): This module operates off clock s1clk. It also uses "decimal_pt" to "shape" the switch response. - tonegen employs 'clk_i', the high-frequency clock, as tonegen likes to divide this frequency by different numbers based on the tone being played. - durc employs only the s8clk - player employs only the s8clk - petcore employs only the s8clk Required Files: --------------- The following files are required to be present before you can make a project out of petsdram.vhd: - petsdram.ucf : User constraints file for pins - common.vhd : Common VHDL definitions - petsdram.vhd : Top-level VHDL design - sdec.vhd : Switch decoder FSMs - tonegen.vhd : Frequency divider - durc.vhd : Duration counter - playersdram.vhd : Music player - xsasdramcntl.vhd : The SDRAM controller courtesy of www.xess.com - sdramcntl.vhd : The above SDRAM controller uses this - xsa_display.vhd : The 7-segment display unit - petcoresdram.vhd : The "core" of the pet circuit - basically the "seat of control" - sclkgen.vhd : The clock generator - feeds divided clocks to others Reading suggestions for the code: ================================= Read petsdram.vhd. Then read petcoresdram.vhd. Then read playersdram.vhd. Then read durc.vhd. Then read the other modules in some order. -- end