diff --git a/README.md b/README.md index 526835b..5836802 100644 --- a/README.md +++ b/README.md @@ -1 +1,7 @@ -# QubitPulseGenerator \ No newline at end of file +# QubitPulseGenerator + +- Run simulation (Questa or Vivado xsim): `cd scripts && vsim -c -do sim.do` or `vivado -mode batch -source sim.do`. +- Create Vivado project (XC7A100T): `create_project pulse_gen ./build -part xc7a100tcsg324-1; add_files ../rtl/pulse_gen.sv; add_files -fileset constrs_1 ../constraints/pulse_gen.xdc`. +- Run implementation: `launch_runs impl_1 -to_step write_bitstream; wait_on_run impl_1`. +- Generate timing report: `report_timing_summary -delay_type max -report_unconstrained -max_paths 10 -file timing_summary.rpt`. +- Verify in `timing_summary.rpt` that `WNS >= 0` and reported Fmax is greater than 100 MHz. diff --git a/constraints/pulse_gen.xdc b/constraints/pulse_gen.xdc new file mode 100644 index 0000000..92ee5c3 --- /dev/null +++ b/constraints/pulse_gen.xdc @@ -0,0 +1,3 @@ +create_clock -name clk -period 10.000 [get_ports clk] +set_property PACKAGE_PIN E3 [get_ports clk] +set_property IOSTANDARD LVCMOS33 [get_ports clk] diff --git a/rtl/pulse_gen.sv b/rtl/pulse_gen.sv new file mode 100644 index 0000000..c1a00f8 --- /dev/null +++ b/rtl/pulse_gen.sv @@ -0,0 +1,45 @@ +module pulse_gen #( + parameter int WIDTH_BITS = 8, + parameter int PERIOD_BITS = 16 +) ( + input logic clk, + input logic rst_n, + input logic enable, + input logic [WIDTH_BITS-1:0] width, + input logic [PERIOD_BITS-1:0] period, + output logic pulse, + output logic busy +); + + logic [PERIOD_BITS-1:0] count; + logic [PERIOD_BITS-1:0] width_eff; + + always_comb begin + if (width >= period) + width_eff = period; + else + width_eff = {{(PERIOD_BITS-WIDTH_BITS){1'b0}}, width}; + end + + always_ff @(posedge clk or negedge rst_n) begin + if (!rst_n) begin + count <= '0; + pulse <= 1'b0; + busy <= 1'b0; + end else if (!enable || (period == '0) || (width == '0)) begin + count <= '0; + pulse <= 1'b0; + busy <= 1'b0; + end else begin + busy <= 1'b1; + + if (count == (period - 1'b1)) + count <= '0; + else + count <= count + 1'b1; + + pulse <= (count < width_eff); + end + end + +endmodule diff --git a/scripts/sim.do b/scripts/sim.do new file mode 100644 index 0000000..f852566 --- /dev/null +++ b/scripts/sim.do @@ -0,0 +1,12 @@ +if {[info commands vlog] ne ""} { + vlib work + vlog -sv ../rtl/pulse_gen.sv ../sim/tb_pulse_gen.sv + vsim -c tb_pulse_gen -do "run -all; quit -f" +} elseif {[info commands xvlog] ne ""} { + file mkdir xsim + xvlog -sv ../rtl/pulse_gen.sv ../sim/tb_pulse_gen.sv + xelab tb_pulse_gen -s tb_pulse_gen_sim + xsim tb_pulse_gen_sim -runall +} else { + puts "No supported simulator commands found (vlog/vsim or xvlog/xelab/xsim)." +} diff --git a/sim/results.txt b/sim/results.txt new file mode 100644 index 0000000..7ff4f4b --- /dev/null +++ b/sim/results.txt @@ -0,0 +1,6 @@ +Expected waveform summary (100MHz clock, 10ns period): +- During active-low reset: pulse=0, busy=0. +- width=1, period=2, enable=1: pulse alternates high/low every clock (10ns high, 10ns low), busy=1. +- enable->0: pulse drops to 0 immediately on next clock, busy=0, counter resets. +- width=255, period=65535, enable=1: pulse high for 255 cycles (2.55us), then low for 65280 cycles (652.8us), repeating. +- Reassert reset during run: pulse=0, busy=0, generation restarts from cycle 0 when enabled again. diff --git a/sim/tb_pulse_gen.sv b/sim/tb_pulse_gen.sv new file mode 100644 index 0000000..a33e109 --- /dev/null +++ b/sim/tb_pulse_gen.sv @@ -0,0 +1,99 @@ +`timescale 1ns/1ps + +module tb_pulse_gen; + localparam int WIDTH_BITS = 8; + localparam int PERIOD_BITS = 16; + + logic clk; + logic rst_n; + logic enable; + logic [WIDTH_BITS-1:0] width; + logic [PERIOD_BITS-1:0] period; + logic pulse; + logic busy; + + int errors; + + pulse_gen #( + .WIDTH_BITS(WIDTH_BITS), + .PERIOD_BITS(PERIOD_BITS) + ) dut ( + .clk(clk), + .rst_n(rst_n), + .enable(enable), + .width(width), + .period(period), + .pulse(pulse), + .busy(busy) + ); + + always #5 clk = ~clk; + + task automatic expect_equal(input string tag, input bit got, input bit exp); + if (got !== exp) begin + $error("%s mismatch: got=%0b exp=%0b at t=%0t", tag, got, exp, $time); + errors++; + end + endtask + + task automatic verify_periodic(input int unsigned w, input int unsigned p, input int unsigned cycles); + int unsigned i; + for (i = 0; i < cycles; i++) begin + @(posedge clk); + expect_equal("busy", busy, (enable && (w != 0) && (p != 0))); + expect_equal("pulse", pulse, ((i % p) < ((w < p) ? w : p))); + end + endtask + + initial begin + clk = 1'b0; + rst_n = 1'b0; + enable = 1'b0; + width = '0; + period = '0; + errors = 0; + + repeat (3) @(posedge clk); + expect_equal("reset pulse", pulse, 1'b0); + expect_equal("reset busy", busy, 1'b0); + + rst_n = 1'b1; + @(posedge clk); + + width = 8'd1; + period = 16'd2; + enable = 1'b1; + verify_periodic(1, 2, 12); + + enable = 1'b0; + repeat (2) @(posedge clk); + expect_equal("disabled pulse", pulse, 1'b0); + expect_equal("disabled busy", busy, 1'b0); + + width = 8'hFF; + period = 16'hFFFF; + enable = 1'b1; + verify_periodic(8'hFF, 16'hFFFF, 66000); + + enable = 1'b0; + @(posedge clk); + expect_equal("toggle low pulse", pulse, 1'b0); + expect_equal("toggle low busy", busy, 1'b0); + + enable = 1'b1; + verify_periodic(8'hFF, 16'hFFFF, 300); + + rst_n = 1'b0; + @(posedge clk); + expect_equal("reset2 pulse", pulse, 1'b0); + expect_equal("reset2 busy", busy, 1'b0); + rst_n = 1'b1; + + if (errors == 0) + $display("PASS: all checks passed"); + else + $fatal(1, "FAIL: %0d checks failed", errors); + + $finish; + end +endmodule