From a21ac075c2ce7833b958ebe9ffabf77d3170ac5b Mon Sep 17 00:00:00 2001 From: Harald Pretl Date: Sun, 27 Oct 2024 15:37:23 +0100 Subject: [PATCH] Adding DS-modulator --- README.md | 2 + docs/info.md | 9 +++-- info.yaml | 7 ++-- src/dsmod1.v | 91 ++++++++++++++++++++++++++++++++++++++++++ src/tt_um_hpretl_spi.v | 16 ++++++-- test/test.py | 44 +++++++++++++++++--- 6 files changed, 155 insertions(+), 14 deletions(-) create mode 100644 src/dsmod1.v diff --git a/README.md b/README.md index ddd0441..4d0992e 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,6 @@ This project implements a simple SPI where the loaded 16b data can be output in 8b chunks (high and low byte). In addition, a magic cookie detection is implemented (an output goes active on detection of 0xCAFE). +In addition, a first- and second-order delta-sigma modulator is implemented create a simple low-frequency voltage output with 16b. + The input and output list documentation can be found in `info.yaml`. diff --git a/docs/info.md b/docs/info.md index 00094a1..e9bd956 100644 --- a/docs/info.md +++ b/docs/info.md @@ -9,12 +9,15 @@ You can also include images in this folder and reference them in the markdown. E ## How it works -A simple serial 16b register (mini SPI) with magic cookie detection is implemented. +A simple serial 16b register (mini SPI) with magic cookie detection is implemented. In addition, a 16bit sigma-delta modulator of 1st or 2nd order is included. ## How to test -Load the shift register in a serial way. +- Load the shift register in a serial way. +- Check the magic cookie detection. +- Check the digital output (low- and high-byte) of the loaded data word. +- Check the output voltage of the delta-sigma modulator by using an external RC lowpass filter. ## External hardware -Just a way to set digital inputs is needed. A scope for monitoring output signals would be good. +Just a way to set digital inputs is needed. A scope for monitoring output signals would be good. A voltmeter can be used to inspect the DAC output voltage. diff --git a/info.yaml b/info.yaml index 8e15e52..a6a3f03 100644 --- a/info.yaml +++ b/info.yaml @@ -19,6 +19,7 @@ project: source_files: - "tt_um_hpretl_spi.v" - "chain2.v" + - "dsmod1.v" # The pinout of your project. Leave unused pins blank. DO NOT delete or add any pins. pinout: @@ -30,7 +31,7 @@ pinout: ui[4]: "" ui[5]: "" ui[6]: "" - ui[7]: "" + ui[7]: "order of delta-sigma modulator (0 = 1st, 1 = 2nd)" # Outputs uo[0]: "SPI data out (MISO)" @@ -39,8 +40,8 @@ pinout: uo[3]: "" uo[4]: "" uo[5]: "" - uo[6]: "" - uo[7]: "" + uo[6]: "inverted output of delta-sigma modulator" + uo[7]: "output of delta-sigma modulator" # Bidirectional pins uio[0]: "register b0|8" diff --git a/src/dsmod1.v b/src/dsmod1.v new file mode 100644 index 0000000..5385950 --- /dev/null +++ b/src/dsmod1.v @@ -0,0 +1,91 @@ +/* +* SPDX-FileCopyrightText: 2022-2024 Harald Pretl +* Johannes Kepler University, Institute for Integrated Circuits +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* SPDX-License-Identifier: Apache-2.0 +* +* 16b Delta-Sigma Modulator with Single-Bit Output +*/ + +`default_nettype none +`ifndef __DSMOD1__ +`define __DSMOD1__ + +module dsmod1( + input [15:0] i_data, // data in + input i_rst_n, + input i_clk, + input i_mode, // 0 = 1st order, 1 = 2nd order SD-modulator + output reg o_ds, // single-bit SD-modulator output + output wire o_ds_n // plus the complementary output +); + + reg [15:0] accu1; + reg [15:0] accu2; + reg [1:0] accu3; + + reg [1:0] mod2_ctr; // clk divide by 4 for 1st stage of 2nd order mod. + reg [1:0] mod2_out; // output 1st stage + + localparam ORD1 = 1'd0; + localparam ORD2 = 1'd1; + + // provide out and out_n to make levelshifter easier + assign o_ds_n = ~o_ds; + + always @(posedge i_clk or negedge i_rst_n) begin + if (i_rst_n == 1'b0) begin + // reset all registers + accu1 <= 16'b0; + accu2 <= 16'b0; + accu3 <= 2'b0; + o_ds <= 1'b0; + mod2_ctr <= 2'b0; + mod2_out <= 2'b0; + end else begin + // sd-modulator is running + + if (i_mode === ORD1) begin + // delta-sigma modulator 1st order + // this simple structure works if everything + // is UINT + {o_ds,accu1} <= i_data + accu1; + end else if (i_mode === ORD2) begin + // delta-sigma modulator 2nd order + // the first stage runs on clk/4, the second + // stage runs on clk + + if (mod2_ctr === 2'b0) begin + // this only happens every 4th clk + // cycle + {mod2_out,accu1} <= {2'b00,i_data} + + {1'b0,accu1,1'b0} + + 18'h10000 + - {2'b0,accu2}; + accu2 <= accu1; + end + + // this is the clk divider for the 1st stage + mod2_ctr <= mod2_ctr + 1'b1; + + // this simple structure is the 2nd stage + // running on clk + {o_ds,accu3} <= mod2_out + accu3; + end + end + end +endmodule // dsmod1 + +`endif +`default_nettype wire diff --git a/src/tt_um_hpretl_spi.v b/src/tt_um_hpretl_spi.v index 93762d4..83758aa 100644 --- a/src/tt_um_hpretl_spi.v +++ b/src/tt_um_hpretl_spi.v @@ -5,6 +5,7 @@ `default_nettype none `include "chain2.v" +`include "dsmod1.v" module tt_um_hpretl_spi ( input wire [7:0] ui_in, // Dedicated inputs @@ -22,7 +23,7 @@ module tt_um_hpretl_spi ( assign uio_oe = 8'b11111111; // using IO for output assign uio_out = ui_in[3] ? out_w[15:8] : out_w[7:0]; - chain2 dut( + chain2 spi( .i_resetn(rst_n), .i_clk(clk), .i_spi_clk(ui_in[0]), @@ -34,10 +35,19 @@ module tt_um_hpretl_spi ( .o_data(out_w) ); + dsmod1 dac( + .i_data(out_w), + .i_rst_n(rst_n), + .i_clk(clk), + .i_mode(ui_in[7]), + .o_ds(uo_out[7]), + .o_ds_n(uo_out[6]) + ); + // All output pins must be assigned. If not used, assign to 0. - assign uo_out[7:3] = 5'b10000; + assign uo_out[5:3] = 3'b000; // List all unused inputs to prevent warnings - wire _unused = &{ena, uio_in[7:0], ui_in[7:4], 1'b0}; + wire _unused = &{ena, uio_in[7:0], ui_in[6:4], 1'b0}; endmodule // tt_um_hpretl_spi diff --git a/test/test.py b/test/test.py index 0a2e8a0..9099263 100644 --- a/test/test.py +++ b/test/test.py @@ -36,12 +36,12 @@ async def test_project(dut): # Shift cookie in for i in range(16): - #b0 is clk, b1 is dat, b2 is load, b3 is select + # b0 is clk, b1 is dat, b2 is load, b3 is select dut.ui_in.value = 1*0 + 2*load_word_bits[i] + 4*0 + 8*0 await ClockCycles(dut.clk, 3) - #b0 is clk, b1 is dat, b2 is load, b3 is select + # b0 is clk, b1 is dat, b2 is load, b3 is select dut.ui_in.value = 1*1 + 2*load_word_bits[i] + 4*0 + 8*0 await ClockCycles(dut.clk, 3) @@ -49,7 +49,7 @@ async def test_project(dut): assert (dut.uo_out.value & 2) == 0 # serial register is loaded, now store it - #b0 is clk, b1 is dat, b2 is load, b3 is select + # b0 is clk, b1 is dat, b2 is load, b3 is select dut.ui_in.value = 1*0 + 2*0 + 4*0 + 8*0 await ClockCycles(dut.clk, 3) dut.ui_in.value = 1*0 + 2*0 + 4*1 + 8*0 @@ -70,14 +70,48 @@ async def test_project(dut): # Shift 0 in for i in range(16): - #b0 is clk, b1 is dat, b2 is load, b3 is select + # b0 is clk, b1 is dat, b2 is load, b3 is select dut.ui_in.value = 1*0 + 2*0 + 4*0 + 8*0 await ClockCycles(dut.clk, 3) - #b0 is clk, b1 is dat, b2 is load, b3 is select + # b0 is clk, b1 is dat, b2 is load, b3 is select dut.ui_in.value = 1*1 + 2*0 + 4*0 + 8*0 await ClockCycles(dut.clk, 3) assert (dut.uo_out.value & 2) == 2 + + # serial register is loaded, now store it + # b0 is clk, b1 is dat, b2 is load, b3 is select + dut.ui_in.value = 1*0 + 2*0 + 4*0 + 8*0 + await ClockCycles(dut.clk, 3) + dut.ui_in.value = 1*0 + 2*0 + 4*1 + 8*0 + await ClockCycles(dut.clk, 3) + + # Wait a few cycles to watch DAC operation + await ClockCycles(dut.clk, 100) + + # Shift 1 in + for i in range(16): + # b0 is clk, b1 is dat, b2 is load, b3 is select + dut.ui_in.value = 1*0 + 2*1 + 4*0 + 8*0 + + await ClockCycles(dut.clk, 3) + + #b0 is clk, b1 is dat, b2 is load, b3 is select + dut.ui_in.value = 1*1 + 2*1 + 4*0 + 8*0 + + await ClockCycles(dut.clk, 3) + + assert (dut.uo_out.value & 2) == 0 + + # serial register is loaded, now store it + # b0 is clk, b1 is dat, b2 is load, b3 is select + dut.ui_in.value = 1*0 + 2*0 + 4*0 + 8*0 + await ClockCycles(dut.clk, 3) + dut.ui_in.value = 1*0 + 2*0 + 4*1 + 8*0 + await ClockCycles(dut.clk, 3) + + # Wait a few cycles to watch DAC operation + await ClockCycles(dut.clk, 100)