diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bf6fe7a --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# Ignore compiled binaries +* +!/**/ +!*.* +!makefile + +# Ignore gtkwave files +*.vcd + +# Ignore vivado-related files and directories +midpoint_testing +*vivado* diff --git a/Lab 2 Write-Up.pdf b/Lab 2 Write-Up.pdf new file mode 100644 index 0000000..681cf4d Binary files /dev/null and b/Lab 2 Write-Up.pdf differ diff --git a/MidpointTestSequence.txt b/MidpointTestSequence.txt new file mode 100644 index 0000000..efb275c --- /dev/null +++ b/MidpointTestSequence.txt @@ -0,0 +1,16 @@ +Midpoint Test Instructions + + +PIPO: +1. Press button 0 (parallelLoad) to load parallelDataIn (specified as 0xA5, or b10100101). +2. Press button 1 to display the least significant bits. An LED ‘on’ state represents when a bit is 1 (and when it is off, it represents when a bit is 0). LEDs 0 and 2 should be on and LEDs 1 and 3 should be off in order to represent least significant bits ‘0101’. +3. Press button 2 to display the most significant bits. LEDs 0 and 2 should be off and LEDs 1 and 3 should be on in order to represent the most significant bits ‘1010’. + + +SIPO: +1. Place switch 0 in the “low” position. This represents a 0 value bit. When switch 2 is toggled from low to high, the 0 bit will be serially loaded, and the bits in the shift register will shift up one degree from their previous places. +2. Press button 1. LEDs 0 and 2 will now be off, and LEDs 1 and 3 will be on. +3. Press button 2. LEDs 0, 1, and 3 will be off, and LED 2 will be on. +4. Place switch 0 in the high position, which represents a 1 value bit. Toggle switch 1 from low to high to serially load a 1 into the shift register. +5. Press button 1. LEDs 0 and 2 will now be on, and LEDs 1 and 3 will be of. +6. Press button 2. LEDs 1 and 2 will be off, and LEDs 0 and 4 will be on. diff --git a/Work Plan.txt b/Work Plan.txt new file mode 100644 index 0000000..6c70688 --- /dev/null +++ b/Work Plan.txt @@ -0,0 +1,40 @@ +Prava Dhulipalla, Ariana Olson +Lab 2 Work Plan +October 23, 2017 + + +Input Conditioning (6 hours - Finish by 10/24/17) +* Complete inputconditioner.v (1 hour) +* Test bench demonstrates the 3 functions of the input conditioner (2.5 hours) +* Test script executes testbench and makes gtkwave file (0.75 hours) +* Circuit diagram (1 hour) +* Analysis of max input glitch length at 50MHz that will be suppressed by a waittime of 10 (0.75 hours) +Shift Register (4.25 hours - Finish by 10/24/17) +* Complete shiftregister.v (1.5 hours) +* Test bench demonstrating both modes of operation for shift register (2 hours) +* Description of test bench strategy (0.75 hours) +Midpoint Check-in (4 hours - Finish by 10/24/17) +* Top-level module of given structure (0.5 hours) +* Loading to FPGA (0.75 hours) + * Writing wrapper class (0.75 hours) +* Design a test sequence demonstrating successful operation of this portion of lab (1 hour) + * Write a short, written description (0.75 hours) +* Demonstrate to a Ninja (0.25 hours) +SPI Memory (3.5 hours - Finish by 10/27/17) +* Design and implement Finite State Machine (2.5 hours) +* Implement SPI memory meeting specifications defined by waveform (1 hour) +SPI Memory Testing (3.5 hours - Finish by 10/28/17) +* Create a test bench (2 hours) +* Have detailed analysis of testing strategy in report (1.5 hours) +* External testing with Arduino (optional) (+ 1.5 hours if we decide to pursue) +Debugging (4+ hours - Finish by 11/1/17) +* Make appointments with Ben and/or NINJAS +Make it Pretty (1.5 hours - Finish by 11/1/17) +* Makefile, run test script (0.5 hours) +* Clean code and comments (1 hours) +Final Report (0.25 hours - Finish by 11/2/17) +* All analysis and information requested in previous sections (should already be done) +* Reflection of work plan vs reality (0.25 hours) + + +Total time spent: 27.5 hours \ No newline at end of file diff --git a/addresslatch.v b/addresslatch.v new file mode 100644 index 0000000..6cd3b14 --- /dev/null +++ b/addresslatch.v @@ -0,0 +1,25 @@ +//------------------------------------------------------------------------ +// Address Latch +// Positive edge triggered +// If writeEnable is true, addressOut is equal to addressIn +// otherwise, addressOut holds its previous value +//------------------------------------------------------------------------ + +module addresslatch +#( + parameter width = 7 +) +( + input [width-1:0] addressIn, + input writeEnable, + input clk, + output reg [width-1:0] addressOut +); + + always @(posedge clk) begin + if(writeEnable) begin + addressOut[width-1:0] <= addressIn[width-1:0]; + end + end + +endmodule diff --git a/datamemory.v b/datamemory.v index 0d82131..02225af 100644 --- a/datamemory.v +++ b/datamemory.v @@ -17,7 +17,7 @@ module datamemory input [addresswidth-1:0] address, input writeEnable, input [width-1:0] dataIn -) +); reg [width-1:0] memory [depth-1:0]; diff --git a/dflipflop.v b/dflipflop.v new file mode 100644 index 0000000..68a45bd --- /dev/null +++ b/dflipflop.v @@ -0,0 +1,22 @@ +//------------------------------------------------------------------------ +// D Flip Flop +// Positive edge triggered +// If writeEnable is true, q is equal to d +// otherwise, q holds its previous value +//------------------------------------------------------------------------ + +module dflipflop +( + input d, + input writeEnable, + input clk, + output reg q +); + + always @(posedge clk) begin + if(writeEnable) begin + q <= d; + end + end + +endmodule \ No newline at end of file diff --git a/fsm.filter b/fsm.filter new file mode 100644 index 0000000..8800b52 --- /dev/null +++ b/fsm.filter @@ -0,0 +1,8 @@ +# Filter file for GTKWave + +000001 START +000010 RECEIVE +000100 WRITE +001000 READ0 +010000 READ1 +100000 END \ No newline at end of file diff --git a/fsm.t.v b/fsm.t.v new file mode 100644 index 0000000..285424e --- /dev/null +++ b/fsm.t.v @@ -0,0 +1,130 @@ +`timescale 1ns / 1ps + +`include "fsm.v" + +module fsm_test (); + reg sclk, chip_sel, shift_reg_out; + wire miso_buff, dm_we, addr_we, sr_we; + + fsm dut (.sclk(sclk), .chip_sel(chip_sel), .shift_reg_out(shift_reg_out), + .miso_buff(miso_buff), .dm_we(dm_we), .addr_we(addr_we), .sr_we(sr_we)); + + + always begin + #5 sclk = ~sclk; + end + + initial begin + $dumpfile("fsm.vcd"); + $dumpvars(); + + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // chip_sel = 1; shift_reg_out = 1; #10 + // $displayb("miso_buff: %b", miso_buff); + // $displayb("dm_we: %b", dm_we); + // $displayb("addr_we: %b", addr_we); + // $displayb("sr_we: %b", sr_we); + + // chip_sel = 0; + // shift_reg_out = 0; #70 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // shift_reg_out = 0; #10 + // sclk = 1; #5 + // sclk = 0; #5 + + // shift_reg_out = 0; #80 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + + // chip_sel = 1; #5 + + + + sclk = 0; + chip_sel = 0; + shift_reg_out = 0; #80 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; + + shift_reg_out = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + // sclk = 1; #5 + // sclk = 0; #5 + + $displayb("miso_buff: %b", miso_buff); + $displayb("dm_we: %b", dm_we); + $displayb("addr_we: %b", addr_we); + $displayb("sr_we: %b", sr_we); + + $finish(); + end + +endmodule \ No newline at end of file diff --git a/fsm.v b/fsm.v new file mode 100644 index 0000000..fbe57a7 --- /dev/null +++ b/fsm.v @@ -0,0 +1,155 @@ +// ideas for code +// same counter for each one --> just reset back to zero +// if chip select high (use always statement) --> + + + +module fsm +( + input sclk, + input chip_sel, + input shift_reg_out, + output reg miso_buff, + output reg dm_we, + output reg addr_we, + output reg sr_we +); + + reg [3:0] count; + reg [7:0] outputval; + reg rw; + + // State encoding (one hot) + reg [5:0] state; + localparam STATE_START = 6'b000001, STATE_RECIEVE = 6'b000010, + STATE_WRITE = 6'b000100, STATE_READ0 = 6'b001000, + STATE_READ1 = 6'b010000, STATE_END = 6'b100000; + + // State logic + always @(posedge sclk) begin + // always @(sclk) begin + if (state === 6'bx) begin + state <= STATE_START; + end + + else begin + + case(state) + + STATE_START: begin + if (chip_sel == 1) begin + state <= STATE_START; + end + else if (chip_sel == 0) begin + state <= STATE_RECIEVE; + count <= 4'd0; + end + end + + STATE_RECIEVE: begin + if (count < 4'd7) begin + count <= count + 4'd1; + end + else if (count == 4'd7) begin + // rw <= shift_reg_out; + if (shift_reg_out === 1'b1) begin + state <= STATE_READ0; + count <= 4'd0; + end + else if (shift_reg_out === 1'b0) begin + state <= STATE_WRITE; + count <= 4'd0; + end + else if (shift_reg_out === 1'bx) begin + count <= count; + end + end + end + + STATE_WRITE: begin + if (count < 4'd7) begin + count <= count + 4'd1; + end + else if (count == 4'd7) begin + state <= STATE_END; + count <= 4'b0; + end + end + + STATE_READ0: begin + state <= STATE_READ1; + end + + STATE_READ1: begin + if (count < 4'd7) begin + count <= count + 4'd1; + end + else if (count == 4'd7) begin + state <= STATE_END; + count <= 4'b0; + end + end + + STATE_END: begin + if (chip_sel == 1) begin + state <= STATE_START; + end + end + + endcase + end + end + + + // Output logic + always @(state) begin + + case (state) + + STATE_START: begin + addr_we <= 1'b0; + dm_we <= 1'b0; + miso_buff <= 1'b0; + sr_we <= 1'b0; + end + + STATE_RECIEVE: begin + addr_we <= 1'b1; + dm_we <= 1'b0; + miso_buff = 1'b0; + sr_we = 1'b0; + end + + STATE_WRITE: begin + addr_we <= 1'b0; + dm_we <= 1'b1; + miso_buff <= 1'b0; + sr_we <= 1'b0; + end + + STATE_READ0: begin + addr_we <= 1'b0; + dm_we <= 1'b0; + miso_buff <= 1'b0; + sr_we <= 1'b1; + end + + STATE_READ1: begin + addr_we <= 1'b0; + dm_we <= 1'b0; + miso_buff <= 1'b1; + sr_we <= 1'b0; + end + + STATE_END: begin + addr_we <= 1'b0; + dm_we <= 1'b0; + miso_buff <= 1'b0; + sr_we <= 1'b0; + end + + endcase + + end + +endmodule \ No newline at end of file diff --git a/inputconditioner.t.v b/inputconditioner.t.v index 2814163..26d1aa1 100644 --- a/inputconditioner.t.v +++ b/inputconditioner.t.v @@ -1,29 +1,128 @@ + //------------------------------------------------------------------------ // Input Conditioner test bench +// Test the functions of the input condidtioner +// - Input Synchronization +// - Input Debouncing +// - Edge Detection //------------------------------------------------------------------------ +`include "inputconditioner.v" + + module testConditioner(); - reg clk; - reg pin; + reg clk; + reg pin; wire conditioned; wire rising; wire falling; - - inputconditioner dut(.clk(clk), - .noisysignal(pin), - .conditioned(conditioned), - .positiveedge(rising), - .negativeedge(falling)) + + inputconditioner dut ( + .clk (clk), + .noisysignal (pin), + .conditioned (conditioned), + .positiveedge(rising), + .negativeedge(falling) + ); // Generate clock (50MHz) - initial clk=0; - always #10 clk=!clk; // 50MHz Clock - + initial clk = 0; + always #10 clk = !clk; // 50MHz Clock + initial begin - // Your Test Code - // Be sure to test each of the three conditioner functions: - // Synchronization, Debouncing, Edge Detection + + $dumpfile("input_conditioner.vcd"); + $dumpvars(); + //Test case 1: Input Synchronization (synchronizes signal with the internal clock) + pin = 0; #5 + if (conditioned == 0 && clk == 0) + $display("Test Case 1a failed: pin changed outside of clock cycle %b", clk); + + pin = 1; #5 + if (conditioned == 1 && clk == 0) + $display("Test Case 1a failed: pin changed outside of clock cycle"); + pin = 0; #15 + if (conditioned != 0 && clk == 1) + $display("Test Case 1b failed: pin not changed inside of clock cycle"); + + pin = 1; #15 + if (conditioned != 1 && clk == 1) + $display("Test Case 1c failed: pin not changed inside of clock cycle"); + + + + // Test Case 2 + 3: Debouncing + + // Test Case 2: Noisy high input signal + pin = 0; #300 + pin = 1; #5 + pin = 0; #5 + pin = 1; #5 + pin = 0; #5 + pin = 1; #5 + pin = 0; #5 + pin = 1; #5 + pin = 0; #5 + pin = 1; #5 + pin = 0; #5 + pin = 1; #250 + + // Expect the conditioned output to be high when the pin input has stabilized. + if (conditioned != 1) begin + $display("Test Case X failed. conditioned output is not high"); + end + + // Test Case 3: Noisy low input signal + pin = 0; #5 + pin = 1; #5 + pin = 0; #5 + pin = 1; #5 + pin = 0; #5 + pin = 1; #5 + pin = 0; #5 + pin = 1; #5 + pin = 0; #5 + pin = 1; #5 + pin = 0; #250 + + // Expect the conditioned output to be low when the pin input has stabilized. + if (conditioned != 0) begin + $display("Test Case X failed. conditioned output is not low"); + end + + // Test Case 4 + 5: Edge Detection + + pin = 0; #300 + pin = 1; #300 + pin = 0; #300 + pin = 1; #300 + pin = 0; #300 + pin = 1; #300 + pin = 0; #300 + + $finish(); +end + + // Test Case 4: Positive Edge Detection + + always @(posedge conditioned) begin + #5; + if (rising != 1 && $time > 100) begin + $display("Test Case 3 failed: rising edge not detected at time %t", $time); + $display("rising: %b", rising); + end + end + + // Test Case 5: Negative Edge Detection + + always @(negedge conditioned) begin + #5; + if (falling != 1 && $time > 100) begin + $display("Test Case 4 failed: falling edge not detected at time %t", $time); + $display("falling: %b", falling); + end + end endmodule diff --git a/inputconditioner.v b/inputconditioner.v index 736a866..a77d9da 100644 --- a/inputconditioner.v +++ b/inputconditioner.v @@ -21,12 +21,20 @@ output reg negativeedge // 1 clk pulse at falling edge of conditioned reg synchronizer0 = 0; reg synchronizer1 = 0; - always @(posedge clk ) begin + always @(posedge clk) begin + positiveedge <= 0; + negativeedge <= 0; if(conditioned == synchronizer1) counter <= 0; else begin - if( counter == waittime) begin + if(counter == waittime) begin counter <= 0; + if (conditioned == 1 && synchronizer1 == 0) begin + negativeedge <= 1; + end + if (conditioned == 0 && synchronizer1 == 1) begin + positiveedge <= 1; + end conditioned <= synchronizer1; end else diff --git a/makefile b/makefile new file mode 100644 index 0000000..7973edb --- /dev/null +++ b/makefile @@ -0,0 +1,22 @@ +all: inputconditioner shiftregister midpoint addresslatch dflipflop fsm spimemory + +inputconditioner: inputconditioner.t.v inputconditioner.v + iverilog -Wall -o inputconditioner inputconditioner.t.v + +shiftregister: shiftregister.t.v shiftregister.v + iverilog -Wall -o shiftregister shiftregister.t.v + +midpoint: midpoint.v + iverilog -Wall -o midpoint midpoint.v + +addresslatch: addresslatch.v + iverilog -Wall -o addresslatch addresslatch.v + +dflipflop: dflipflop.v + iverilog -Wall -o dflipflop dflipflop.v + +fsm: fsm.v + iverilog -Wall -o fsm fsm.v + +spimemory: spimemory.t.v spimemory.v + iverilog -Wall -o spimemory spimemory.t.v \ No newline at end of file diff --git a/midpoint.v b/midpoint.v new file mode 100644 index 0000000..4f042cd --- /dev/null +++ b/midpoint.v @@ -0,0 +1,142 @@ +//-------------------------------------------------------------------------------- +// Wrapper for Lab 2: Midpoint Check +// +// Rationale: +// This wrapper module allows for testing the PIPO and SIPO functionality of +// the shift register with conditioned inputs. +// +// Usage: +// btn0 - load a constant value 8'b10100101 to the shift register +// btn1 - Display the 4 least significant bits of the Parallel Out port +// btn2 - Display the 4 most significant bits of the Parallel Out port +// sw0 - toggle the serial input between low and high +// sw1 - toggle to shift the serial input to the LSB of the shift register +// +// Note: Buttons, switches, and LEDs have the least-significant (0) position +// on the right. +//-------------------------------------------------------------------------------- + +`timescale 1ns / 1ps +`include "inputconditioner.v" +`include "shiftregister.v" + + +//-------------------------------------------------------------------------------- +// Basic building block modules +//-------------------------------------------------------------------------------- + +// JK flip-flop +module jkff1 +( + input trigger, + input j, + input k, + output reg q +); + always @(posedge trigger) begin + if(j && ~k) begin + q <= 1'b1; + end + else if(k && ~j) begin + q <= 1'b0; + end + else if(k && j) begin + q <= ~q; + end + end +endmodule + +// Two-input MUX with parameterized bit width (default: 1-bit) +module mux2 #( parameter W = 1 ) +( + input[W-1:0] in0, + input[W-1:0] in1, + input sel, + output[W-1:0] out +); + // Conditional operator - http://www.verilog.renerta.com/source/vrg00010.htm + assign out = (sel) ? in1 : in0; +endmodule + +module midpoint +( + input clk, + input button0, + input switch0, + input switch1, + input [7:0] parallelIn, + output [7:0] ledState +); + + // Input Conditioner for Button 0 + + wire b0_conditioned; + wire b0_positiveedge; + wire b0_negativeedge; + + inputconditioner b0(clk, button0, b0_conditioned, b0_positiveedge, b0_negativeedge); + + // Input Conditioner for Switch 0 + + wire s0_conditioned; + wire s0_positiveedge; + wire s0_negativeedge; + + inputconditioner s0(clk, switch0, s0_conditioned, s0_positiveedge, s0_negativeedge); + + // Input Conditioner for Switch 1 + + wire s1_conditioned; + wire s1_positiveedge; + wire s1_negativeedge; + + inputconditioner s1(clk, switch1, s1_conditioned, s1_positiveedge, s1_negativeedge); + + // Shift register + wire sr_serialOut; + + shiftregister sr(clk, s1_positiveedge, b0_negativeedge, parallelIn, s0_conditioned, ledState, sr_serialOut); + +endmodule + + +//-------------------------------------------------------------------------------- +// Main Lab 0 wrapper module +// Interfaces with switches, buttons, and LEDs on ZYBO board. Allows for two +// 4-bit operands to be stored, and two results to be alternately displayed +// to the LEDs. +// +// You must write the FullAdder4bit (in your adder.v) to complete this module. +// Challenge: write your own interface module instead of using this one. +//-------------------------------------------------------------------------------- + +module lab0_wrapper +( + input clk, + input [1:0] sw, + input [2:0] btn, + output [3:0] led +); + + wire[7:0] res; // Full parallel output of shift register + wire[3:0] res0, res1; // Output display options: 4 most or 4 least significant bits + wire res_sel; // Select between display options + + // Capture button input to switch which MUX input to LEDs + jkff1 src_sel(.trigger(clk), .j(btn[2]), .k(btn[1]), .q(res_sel)); + mux2 #(4) output_select(.in0(res0), .in1(res1), .sel(res_sel), .out(led)); + + parameter parallelIn = 8'hA5; + midpoint mid (.clk(clk), .button0(btn[0]), .switch0(sw[0]), .switch1(sw[1]), .parallelIn(parallelIn), .ledState(res)); + + // Assign bits of second display output to show carry out and overflow + assign res0[0] = res[0]; + assign res0[1] = res[1]; + assign res0[2] = res[2]; + assign res0[3] = res[3]; + assign res1[0] = res[4]; + assign res1[1] = res[5]; + assign res1[2] = res[6]; + assign res1[3] = res[7]; + +endmodule \ No newline at end of file diff --git a/shiftregister.t.v b/shiftregister.t.v index abe5b48..5362313 100644 --- a/shiftregister.t.v +++ b/shiftregister.t.v @@ -1,7 +1,17 @@ //------------------------------------------------------------------------ // Shift Register test bench +// Tests for the following functions of the shift register: +// 1) The shift register advances one position on a peripheral clock edge. +// - serialDataIn loaded into the LSB. +// - The rest of the bits shift up by one position. +// 2) When parallelLoad is asserted, the shift register will take the value of parallelDataIn. +// - Data is not loaded into the shift register from the serialDataInPort if parallelLoad is true. +// 3) serialDataOut will always present the MSB of the shift register. +// 4) parallelDataOut always presents the entirety of the contents of the shift register //------------------------------------------------------------------------ +`include "shiftregister.v" + module testshiftregister(); reg clk; @@ -21,8 +31,143 @@ module testshiftregister(); .parallelDataOut(parallelDataOut), .serialDataOut(serialDataOut)); + initial clk = 0; + always #10 clk = !clk; // 50MHz Clock + + initial begin - // Your Test Code + $dumpfile("shiftregister.vcd"); + $dumpvars(); + + // Shift register will serially load data + parallelLoad = 0; + + // Load data into the shift register serially (8'b01010101). + peripheralClkEdge = 0; + serialDataIn = 0; #50 + peripheralClkEdge = 1; #10 + peripheralClkEdge = 0; + serialDataIn = 1; #50 + peripheralClkEdge = 1; #10 + peripheralClkEdge = 0; + serialDataIn = 0; #50 + peripheralClkEdge = 1; #10 + peripheralClkEdge = 0; + serialDataIn = 1; #50 + peripheralClkEdge = 1; #10 + peripheralClkEdge = 0; + serialDataIn = 0; #50 + peripheralClkEdge = 1; #10 + peripheralClkEdge = 0; + serialDataIn = 1; #50 + peripheralClkEdge = 1; #10 + peripheralClkEdge = 0; + serialDataIn = 0; #50 + peripheralClkEdge = 1; #10 + peripheralClkEdge = 0; + serialDataIn = 1; #50 + peripheralClkEdge = 1; #10 + peripheralClkEdge = 0; #50 + + // Test Case 0: the parallelDataOut port displays the entire contents of the shift register. + if (parallelDataOut != dut.shiftregistermem) begin + $display("Test Case 0 failed: parallelDataOut does not match the contents of the shift register."); + end + + // Test Case 1: Serially load data into the shift register. + // After 8 peripheral clock cycles, the shift register should contain the bits that were loaded in above. + if (parallelDataOut != 8'b01010101) begin + $display("Test Case 1 failed: parallelDataOut does not match the serial input sequence at time %t", $time); + $displayb("parallelDataOut: %b", parallelDataOut); + end + + + serialDataIn = 1; + peripheralClkEdge = 1; #10 + peripheralClkEdge = 0; #50 + + // Test Case 2: at the peripheral clock edge, serialData in is loaded into the LSB of the shift register + // and the rest of the bits shift over one position + if (parallelDataOut != 8'b10101011) begin + $display("Test Case 2 failed: parallelDataOut not shifted one position from the previous reading."); + $displayb("parallelDataOut: %b", parallelDataOut); + end + + #1000; + + // Shift register should load all data at once in parallel. + parallelLoad = 1; + + // Parallel load data (8'b00000000). + parallelDataIn = 8'b00000000; #50 + peripheralClkEdge = 1; #10 + + // Test Case 3: Load parallel data. + // ParallelDataIn should have been loaded into the shift register. + if (parallelDataOut != parallelDataIn) begin + $display("Test Case 3 failed: parallelDataIn does not match parallelDataOut despite enabled parallelLoad %t", $time); + $displayb("parallelDataOut: %b", parallelDataOut); + end + + // Present data to the serialDataIn port. This should not be stored in the register + peripheralClkEdge = 0; + serialDataIn = 0; #50 + peripheralClkEdge = 1; #10 + peripheralClkEdge = 0; + serialDataIn = 1; #50 + peripheralClkEdge = 1; #10 + peripheralClkEdge = 0; + serialDataIn = 0; #50 + peripheralClkEdge = 1; #10 + peripheralClkEdge = 0; + serialDataIn = 1; #50 + peripheralClkEdge = 1; #10 + peripheralClkEdge = 0; + serialDataIn = 0; #50 + peripheralClkEdge = 1; #10 + peripheralClkEdge = 0; + serialDataIn = 1; #50 + peripheralClkEdge = 1; #10 + peripheralClkEdge = 0; + serialDataIn = 0; #50 + peripheralClkEdge = 1; #10 + peripheralClkEdge = 0; + serialDataIn = 1; #50 + + + // Test Case 4: ParallelLoad blocks serial loading into shift register. + // After 8 perpheral clock cycles with parallelLoad set high, ParallelDataOut should match the input parallel data. + if (parallelDataOut != parallelDataIn) begin + $display("Test Case 4 failed: parallelDataIn does not match parallelDataOut despite enabled parallelLoad %t", $time); + $displayb("parallelDataOut: %b", parallelDataOut); + end + + // Load parallel data into register (8'b01010101). + parallelLoad = 1; + parallelDataIn = 8'b01010101; #50 + + // Test Case 5: parallel in serial out. + // The most significant bit of the parallel data loaded in should match the value of serialDataOut. + if (serialDataOut != parallelDataIn[7]) begin + $display("Test Case 5 failed: serial out does not match parallel in at time %t", $time); + $displayb("serialDataOut: %b", serialDataOut); + end + + // Shift register should serially load data when parallelLoad is low. + parallelLoad = 0; + + // Present parallel load data to the parallelDataIn port. + parallelDataIn = 8'b11010101; #50 + + // Test Case 6: Only parallel load data when parallelLoad is high. + // The parallelDataOut port should not have the value of the parallelDataIn port. + if (parallelDataOut == 8'b11010101) begin + $display("Test Case 6 failed: parallelDataOut changed without parallelLoad enabled %t", $time); + $displayb("serialDataOut: %b", serialDataOut); + end + + $finish(); + end endmodule diff --git a/shiftregister.v b/shiftregister.v index b4ec057..1060014 100644 --- a/shiftregister.v +++ b/shiftregister.v @@ -6,20 +6,28 @@ // - parallel in, serial out //------------------------------------------------------------------------ -module shiftregister -#(parameter width = 8) -( -input clk, // FPGA Clock -input peripheralClkEdge, // Edge indicator -input parallelLoad, // 1 = Load shift reg with parallelDataIn -input [width-1:0] parallelDataIn, // Load shift reg in parallel -input serialDataIn, // Load shift reg serially -output [width-1:0] parallelDataOut, // Shift reg data contents -output serialDataOut // Positive edge synchronized +module shiftregister #(parameter width = 8) ( + input clk, // FPGA Clock + input peripheralClkEdge, // Edge indicator + input parallelLoad, // 1 = Load shift reg with parallelDataIn + input [width-1:0] parallelDataIn, // Load shift reg in parallel + input serialDataIn, // Load shift reg serially + output reg [width-1:0] parallelDataOut, // Shift reg data contents + output reg serialDataOut // Positive edge synchronized ); - reg [width-1:0] shiftregistermem; - always @(posedge clk) begin - // Your Code Here - end + reg [width-1:0] shiftregistermem; + + always @(posedge clk) begin + if (parallelLoad) begin + shiftregistermem <= parallelDataIn; + end + else if (peripheralClkEdge) begin + shiftregistermem <= {parallelDataOut[width-2:0], serialDataIn}; + end + + parallelDataOut <= shiftregistermem; + serialDataOut <= shiftregistermem[width-1]; + end + endmodule diff --git a/spimemory.t.v b/spimemory.t.v new file mode 100644 index 0000000..f8b115c --- /dev/null +++ b/spimemory.t.v @@ -0,0 +1,249 @@ +`include "spimemory.v" + +module testspimemory (); + reg mosi_pin; + reg sclk; + reg cs; + reg clk; + wire miso_pin; + + spimemory dut (clk, sclk, cs, miso_pin, mosi_pin); + + initial clk = 0; + always #10 clk = !clk; + + initial begin + $dumpfile("spimemory.vcd"); + $dumpvars(0, testspimemory, dut.dm.memory[0], dut.dm.memory[1]); + + // One cycle to get to first state + cs = 1; // keep cs high until in state + sclk = 0; #1000 + sclk = 1; #1000 + + if(dut.fsm.state != 6'b000001) begin + $displayb("Test failed: the state is expected to be start, state is actually %b at time %t", dut.fsm.state, $time); + end + + // Start presenting address bits 7'b1010101 + cs = 0; + sclk = 0; mosi_pin = 0; #1000 + sclk = 1; #1000 + + if(dut.fsm.state != 6'b000010) begin + $displayb("Test failed: the state is expected to be receive, state is actually %b at time %t", dut.fsm.state, $time); + end + + sclk = 0; mosi_pin = 0; #1000 + sclk = 1; #1000 + + sclk = 0; mosi_pin = 0; #1000 + sclk = 1; #1000 + + sclk = 0; mosi_pin = 0; #1000 + sclk = 1; #1000 + + sclk = 0; mosi_pin = 0; #1000 + sclk = 1; #1000 + + sclk = 0; mosi_pin = 0; #1000 + sclk = 1; #1000 + + sclk = 0; mosi_pin = 0; #1000 + sclk = 1; #1000 + + if (dut.dm.address != 7'b0000000) begin + $display("Test failed: the address to be written to does not match the expected address."); + end + + // Indicate write state + sclk = 0; mosi_pin = 0; #1000 + sclk = 1; #1000 + + // Start presenting data bits 8'b 10101010 + sclk = 0; mosi_pin = 1; #1000 + sclk = 1; #1000 + + if(dut.fsm.state != 6'b000100) begin + $displayb("Test failed: the state is expected to be write, state is actually %b at time %t", dut.fsm.state, $time); + end + + sclk = 0; mosi_pin = 0; #1000 + sclk = 1; #1000 + + sclk = 0; mosi_pin = 1; #1000 + sclk = 1; #1000 + + sclk = 0; mosi_pin = 0; #1000 + sclk = 1; #1000 + + sclk = 0; mosi_pin = 1; #1000 + sclk = 1; #1000 + + sclk = 0; mosi_pin = 0; #1000 + sclk = 1; #1000 + + sclk = 0; mosi_pin = 1; #1000 + sclk = 1; #1000 + + sclk = 0; mosi_pin = 0; #1000 + sclk = 1; #1000 + + if (dut.dm.memory[0] != 8'b10101010) begin + $display("Test case failed: the data written to the memory does not match the expected data."); + end + + // $display("%t",$time); + + + // Chip select goes high. + sclk = 0; cs = 1; #1000 + sclk = 1; #1000 + + if(dut.fsm.state != 6'b100000) begin + $display("Test failed: the state is expected to be end, state is actually %b at time %t", dut.fsm.state, $time); + end + + sclk = 0; #1000 + sclk = 1; #1000 + + sclk = 0; #1000 + sclk = 1; #1000 + + sclk = 0; #1000 + sclk = 1; #1000 + + sclk = 0; #1000 + sclk = 1; #1000 + + sclk = 0; #1000 + sclk = 1; #1000 + + // Chip select goes low. + cs = 0; + + if(dut.fsm.state != 6'b000001) begin + $display("Test failed: the state is expected to be start, state is actually %b at time %t", dut.fsm.state, $time); + end + + // Start presenting address bits '7b 1010101 + sclk = 0; mosi_pin = 0; #1000 + sclk = 1; #1000 + + if(dut.fsm.state != 6'b000010) begin + $display("Test failed: the state is expected to be receive, state is actually %b at time %t", dut.fsm.state, $time); + end + + sclk = 0; mosi_pin = 0; #1000 + sclk = 1; #1000 + + sclk = 0; mosi_pin = 0; #1000 + sclk = 1; #1000 + + sclk = 0; mosi_pin = 0; #1000 + sclk = 1; #1000 + + sclk = 0; mosi_pin = 0; #1000 + sclk = 1; #1000 + + sclk = 0; mosi_pin = 0; #1000 + sclk = 1; #1000 + + sclk = 0; mosi_pin = 0; #1000 + sclk = 1; #1000 + + if (dut.dm.address != 7'b0000000) begin + $display("Test failed: the address to be written to does not match the expected address."); + end + + // Indicate read state + sclk = 0; mosi_pin = 1; #1000 + sclk = 1; #1000 + + if (dut.dataMemOut != dut.dm.memory[0]) begin + $display("Test failed: the data and memory do not match"); + $display("data: %b, mem: %b", dut.dataMemOut, dut.dm.memory[0]); + end + + + // Cycle through to push all data to serialOut + sclk = 0; #1000 + sclk = 1; #1000 + if (miso_pin != dut.dm.memory[0][0]) begin + $display("Test failed at time %t: output of shift register does not match the value of the memory at the correspondong address. miso_pin: %b, memory: %b", $time, miso_pin, dut.dm.memory[0][0]); + end + + if(dut.fsm.state != 6'b001000) begin + $display("Test failed: the state is expected to be read0, state is actually %b at time %t", dut.fsm.state, $time); + end + + sclk = 0; #1000 + sclk = 1; #1000 + if (miso_pin != dut.dm.memory[0][1]) begin + $display("Test failed at time %t: output of shift register does not match the value of the memory at the correspondong address. miso_pin: %b, memory: %b", $time, miso_pin, dut.dm.memory[0][1]); + end + + if(dut.fsm.state != 6'b010000) begin + $display("Test failed: the state is expected to be read1, state is actually %b at time %t", dut.fsm.state, $time); + end + + sclk = 0; #1000 + sclk = 1; #1000 + if (miso_pin != dut.dm.memory[0][2]) begin + $display("Test failed at time %t: output of shift register does not match the value of the memory at the correspondong address. miso_pin: %b, memory: %b", $time, miso_pin, dut.dm.memory[0][2]); + end + + sclk = 0; #1000 + sclk = 1; #1000 + if (miso_pin != dut.dm.memory[0][3]) begin + $display("Test failed at time %t: output of shift register does not match the value of the memory at the correspondong address. miso_pin: %b, memory: %b", $time, miso_pin, dut.dm.memory[0][3]); + end + + sclk = 0; #1000 + sclk = 1; #1000 + if (miso_pin != dut.dm.memory[0][4]) begin + $display("Test failed at time %t: output of shift register does not match the value of the memory at the correspondong address. miso_pin: %b, memory: %b", $time, miso_pin, dut.dm.memory[0][4]); + end + + sclk = 0; #1000 + sclk = 1; #1000 + if (miso_pin != dut.dm.memory[0][5]) begin + $display("Test failed at time %t: output of shift register does not match the value of the memory at the correspondong address. miso_pin: %b, memory: %b", $time, miso_pin, dut.dm.memory[0][5]); + end + + sclk = 0; #1000 + sclk = 1; #1000 + if (miso_pin != dut.dm.memory[0][6]) begin + $display("Test failed at time %t: output of shift register does not match the value of the memory at the correspondong address. miso_pin: %b, memory: %b", $time, miso_pin, dut.dm.memory[0][6]); + end + + + sclk = 0; #1000 + sclk = 1; #1000 + if (miso_pin != dut.dm.memory[0][7]) begin + $display("Test failed at time %t: output of shift register does not match the value of the memory at the correspondong address. miso_pin: %b, memory: %b", $time, miso_pin, dut.dm.memory[0][7]); + $display("%b", dut.fsm.state); + end + + sclk = 0; #1000 + sclk = 1; #1000 + + // Chip select goes high again + cs = 1; + sclk = 0; #1000 + sclk = 1; #1000 + + if(dut.fsm.state != 6'b100000) begin + $display("Test failed: the state is expected to be end, state is actually %b at time %t", dut.fsm.state, $time); + end + + sclk = 0; #1000 + sclk = 1; #1000 + sclk = 0; #1000 + sclk = 1; #1000 + + + $finish(); + + end +endmodule \ No newline at end of file diff --git a/spimemory.v b/spimemory.v index c6ed4f7..0a1a7a3 100644 --- a/spimemory.v +++ b/spimemory.v @@ -1,17 +1,105 @@ //------------------------------------------------------------------------ // SPI Memory +// Memory operations happen when chip select is low. +// Memory can be both read from and written to using +// standard spi protocol. //------------------------------------------------------------------------ -module spiMemory +`include "inputconditioner.v" +`include "shiftregister.v" +`include "fsm.v" +`include "datamemory.v" +`include "addresslatch.v" +`include "dflipflop.v" + +module spimemory ( input clk, // FPGA clock input sclk_pin, // SPI clock input cs_pin, // SPI chip select output miso_pin, // SPI master in slave out - input mosi_pin, // SPI master out slave in - output [3:0] leds // LEDs for debugging -) + input mosi_pin//, // SPI master out slave in +); + + // mosi input conditioner wires + + wire mosi_ic_conditioned; + wire mosi_ic_positiveedge; + wire mosi_ic_negativeedge; + + // sclk input conditioner wires + + wire sclk_ic_conditioned; + wire sclk_ic_positiveedge; + wire sclk_ic_negativeedge; + + // chip select input conditioner wires + + wire cs_ic_conditioned; + wire cs_ic_positiveedge; + wire cs_ic_negativeedge; + + // finite state machine wires + + wire miso_buff; + wire dm_we; + wire addr_we; + wire sr_we; + + // data memory wires + + wire [7:0] dataMemOut; + + // shift register wires + + wire [7:0] sr_parallelDataOut; + wire sr_serialDataOut; + + // address latch wires + + wire [6:0] address; + + // d flip flop wires + + wire q; + + + // input conditioner blocks for mosi, sclk, and chip select + + inputconditioner mosi_ic(clk, mosi_pin, mosi_ic_conditioned, mosi_ic_positiveedge, mosi_ic_negativeedge); + + inputconditioner sclk_ic(clk, sclk_pin, sclk_ic_conditioned, sclk_ic_positiveedge, sclk_ic_negativeedge); + + inputconditioner cs_ic(clk, cs_pin, cs_ic_conditioned, cs_ic_positiveedge, cs_ic_negativeedge); + + + // finite state machine + + fsm fsm(sclk_ic_positiveedge, cs_ic_conditioned, sr_parallelDataOut[0], miso_buff, dm_we, addr_we, sr_we); + + + // data memory + + datamemory dm(clk, dataMemOut, address, dm_we, sr_parallelDataOut); + + + // shift register + + shiftregister sr(clk, sclk_ic_positiveedge, sr_we, dataMemOut, mosi_ic_conditioned, sr_parallelDataOut, sr_serialDataOut); + + + // address latch + + addresslatch al(sr_parallelDataOut[7:1], addr_we, clk, address); + + + // d-flip flop + + dflipflop dff(sr_serialDataOut, sclk_ic_negativeedge, clk, q); + + // miso buffer + bufif1 buf1(miso_pin, q, miso_buff); endmodule