SENI
KECANTIKAN & KESEHATAN
KERAJINAN
BUDAYA & SEJARAH
HIBURAN
LINGKUNGAN
MAKANAN & MINUMAN
REKAYASA BALIK
ILMU PENGETAHUAN
OLAHRAGA
TEKNOLOGI
PERANGKAT PAKAI
Your Own chip_core: An 8-bit PWM Dimmer, Tested Before It Is Silicon
Youblob (simulation output) · CC0
Ed

Dibuat oleh

Ed

29. September 2026FI
1
0
0
0
0

Your Own chip_core: An 8-bit PWM Dimmer, Tested Before It Is Silicon

The wafer.space project template arrives with an example design, a 42-bit counter, in src/chip_core.sv. Your own chip starts where you replace it, and a chip cannot be patched after the foundry has made it, so the replacement has to be proven in simulation first. This rung replaces the counter with an 8-bit PWM dimmer. Eight pads read a brightness from 0 to 255, one pad drives the pulse-width-modulated output, and an input pin enables it. It uses the template's own port list, so it drops into the template unchanged, and it fits even the smallest 0.5x0.5 slot. Both the core and its cocotb testbench were run for this blueprint with Icarus Verilog 12.0 and cocotb 2.1.0, and all three tests pass. The picture is that simulation's own output.
Lanjutan
An evening

Instruksi

1

What an 8-bit PWM gives you

Memuat notebook Jupyter…
2

The template this core goes into

The embedded blueprint sets up the project template. This rung writes the core that goes inside it.
3

What chip_core is given

In the template, chip_top.sv holds the pad ring and passes the pads to chip_core, which receives the clock, an active-low reset and three groups of signals: input pads, bidirectional pads and analog pads. The numbers of each depend on the slot: the 0.5x0.5 slot has 4 input, 38 bidirectional and 4 analog pads, the full 1x1 slot 12 input, 40 bidirectional and 2 analog. A bidirectional pad is controlled from inside the core, one bit per pad: bidir_oe enables the output driver, bidir_ie the input buffer, bidir_cs chooses a Schmitt-trigger input, bidir_sl a slow slew rate, and bidir_pu and bidir_pd switch in pull-up and pull-down resistors. The template's example drives every bidirectional pad as an output; this rung turns eight of them into inputs.
4

The core

src/chip_core.sv for the dimmer. The port list is the template's, unchanged. Pads 0 to 7 are inputs with Schmitt triggers; pad 8 and above are outputs, with every pad except 8 held low. The template's example also instantiated two SRAM macros; this core does not use them, so they are gone.
chip_core.svsystemverilog
// SPDX-License-Identifier: Apache-2.0
// Youblob silicon rung 3: an 8-bit PWM dimmer in place of the template's counter.
// Port list unchanged from wafer-space/gf180mcu-project-template src/chip_core.sv.

`default_nettype none

module chip_core #(
    parameter NUM_INPUT_PADS,
    parameter NUM_BIDIR_PADS,
    parameter NUM_ANALOG_PADS
    )(
    `ifdef USE_POWER_PINS
    inout  wire VDD,
    inout  wire VSS,
    `endif

    input  wire clk,       // clock
    input  wire rst_n,     // reset (active low)

    input  wire [NUM_INPUT_PADS-1:0] input_in,   // Input value
    output wire [NUM_INPUT_PADS-1:0] input_pu,   // Pull-up
    output wire [NUM_INPUT_PADS-1:0] input_pd,   // Pull-down

    input  wire [NUM_BIDIR_PADS-1:0] bidir_in,   // Input value
    output wire [NUM_BIDIR_PADS-1:0] bidir_out,  // Output value
    output wire [NUM_BIDIR_PADS-1:0] bidir_oe,   // Output enable
    output wire [NUM_BIDIR_PADS-1:0] bidir_cs,   // Input type (0=CMOS Buffer, 1=Schmitt Trigger)
    output wire [NUM_BIDIR_PADS-1:0] bidir_sl,   // Slew rate (0=fast, 1=slow)
    output wire [NUM_BIDIR_PADS-1:0] bidir_ie,   // Input enable
    output wire [NUM_BIDIR_PADS-1:0] bidir_pu,   // Pull-up
    output wire [NUM_BIDIR_PADS-1:0] bidir_pd,   // Pull-down

    inout  wire [NUM_ANALOG_PADS-1:0] analog  // Analog
);

    // Pad 0..7: the duty value comes IN.  Pad 8: the PWM goes OUT.  The rest: outputs held low.
    localparam DUTY_W = 8;
    localparam PWM_PAD = 8;

    assign input_pu = '0;
    assign input_pd = '0;

    // Output enable only on pads PWM_PAD and above; inputs get their input buffer enabled.
    assign bidir_oe = {{(NUM_BIDIR_PADS-DUTY_W){1'b1}}, {DUTY_W{1'b0}}};
    assign bidir_ie = ~bidir_oe;
    assign bidir_cs = {{(NUM_BIDIR_PADS-DUTY_W){1'b0}}, {DUTY_W{1'b1}}};  // Schmitt trigger on the inputs
    assign bidir_sl = '0;
    assign bidir_pu = '0;
    assign bidir_pd = '0;

    wire enable = input_in[0];
    wire [DUTY_W-1:0] duty = bidir_in[DUTY_W-1:0];

    logic [DUTY_W-1:0] count;
    always_ff @(posedge clk) begin
        if (!rst_n)
            count <= '0;
        else if (enable)
            count <= count + 1'b1;
    end

    // High while the free-running count is below the duty value: duty/256 of every period.
    logic pwm;
    always_ff @(posedge clk) begin
        if (!rst_n)
            pwm <= 1'b0;
        else
            pwm <= enable && (count < duty);
    end

    logic [NUM_BIDIR_PADS-1:0] out;
    always_comb begin
        out = '0;
        out[PWM_PAD] = pwm;
    end
    assign bidir_out = out;

    // Inputs this design does not read (keeps lint quiet, as the template does with _unused).
    logic _unused;
    assign _unused = &{1'b0, input_in[NUM_INPUT_PADS-1:1], bidir_in[NUM_BIDIR_PADS-1:DUTY_W]};

endmodule

`default_nettype wire

Alat yang dibutuhkan:

Komputer DesktopKomputer Desktop
5

The test that proves it

A cocotb testbench that drives chip_core directly. It sets six duty values and counts the high clocks over four whole periods (1,024 clocks), checks that disabling the core holds the output low, and checks the pad directions. Run it with python3 test_pwm.py; Icarus Verilog needs -g2012 for the SystemVerilog and a 1 ns / 1 ps timescale for the 20 ns clock.
test_pwm.pypython
# SPDX-License-Identifier: Apache-2.0
# cocotb testbench for the PWM dimmer core, at the chip_core level (no pads, no PDK needed).
import os
from pathlib import Path

import cocotb
from cocotb.clock import Clock
from cocotb.triggers import ClockCycles, RisingEdge
from cocotb_tools.runner import get_runner

PERIOD = 256          # clock cycles in one PWM period (8-bit count)
PWM_PAD = 8


async def start(dut):
    cocotb.start_soon(Clock(dut.clk, 20, unit="ns").start())   # 50 MHz, as the template's testbench
    dut.input_in.value = 0
    dut.bidir_in.value = 0
    dut.rst_n.value = 0
    await ClockCycles(dut.clk, 5)
    dut.rst_n.value = 1


async def high_cycles(dut, cycles):
    n = 0
    for _ in range(cycles):
        await RisingEdge(dut.clk)
        n += int(dut.bidir_out.value[PWM_PAD])
    return n


@cocotb.test()
async def test_duty(dut):
    """Over whole PWM periods the output is high exactly duty/256 of the time."""
    await start(dut)
    dut.input_in.value = 1                      # enable
    for duty in (0, 1, 64, 128, 200, 255):
        dut.bidir_in.value = duty
        await ClockCycles(dut.clk, PERIOD)      # let one full period pass with the new value
        high = await high_cycles(dut, 4 * PERIOD)
        dut._log.info(f"duty {duty:3d}: high {high} of {4 * PERIOD} cycles")
        assert high == 4 * duty, f"duty {duty}: expected {4 * duty} high cycles, got {high}"


@cocotb.test()
async def test_disable(dut):
    """With the enable input low the output stays low, whatever the duty."""
    await start(dut)
    dut.input_in.value = 0
    dut.bidir_in.value = 200
    assert await high_cycles(dut, 2 * PERIOD) == 0


@cocotb.test()
async def test_pad_directions(dut):
    """Pads 0-7 are inputs (oe low, ie high); pad 8 and above are outputs."""
    await start(dut)
    oe = int(dut.bidir_oe.value)
    ie = int(dut.bidir_ie.value)
    assert oe & 0xFF == 0 and ie & 0xFF == 0xFF
    assert (oe >> 8) & 1 == 1 and (ie >> 8) & 1 == 0


def test_runner():
    here = Path(__file__).resolve().parent
    runner = get_runner(os.getenv("SIM", "icarus"))
    runner.build(
        sources=[here / "chip_core.sv"],
        hdl_toplevel="chip_core",
        parameters={"NUM_INPUT_PADS": 4, "NUM_BIDIR_PADS": 38, "NUM_ANALOG_PADS": 4},  # the 0p5x0p5 slot
        build_args=["-g2012"],
        timescale=("1ns", "1ps"),
        always=True,
    )
    runner.test(hdl_toplevel="chip_core", test_module="test_pwm")


if __name__ == "__main__":
    test_runner()

Alat yang dibutuhkan:

Komputer DesktopKomputer Desktop
6

What the simulation printed

The run for this blueprint, Icarus Verilog 12.0 and cocotb 2.1.0, parameters for the 0.5x0.5 slot: duty 0: high 0 of 1024 cycles duty 1: high 4 of 1024 cycles duty 64: high 256 of 1024 cycles duty 128: high 512 of 1024 cycles duty 200: high 800 of 1024 cycles duty 255: high 1020 of 1024 cycles TESTS=3 PASS=3 FAIL=0 SKIP=0 Every count is exactly four times the duty value. To check that the test can fail, the comparison in the core was changed from count < duty to count <= duty; the test then failed at once (duty 0 gave 4 high cycles, not 0), and the core was put back. In the template itself, replace src/chip_core.sv with this file and update the template's own testbench, cocotb/chip_top_tb.py, whose test still expects the counter; make sim then runs it through the pads and the PDK's models.
7

A core that will not simulate or will not pass

chip_core simulation troubleshooting.

Flow

Loading...
8

Sources and honest limits

**Sources**, read 29 September 2026: the wafer-space/gf180mcu-project-template repository (src/chip_core.sv, src/slot_defines.svh, cocotb/chip_top_tb.py; Apache-2.0), whose port list and pad-control signals this core keeps. **Honest limits.** The simulation is at the chip_core level only, without the pad ring, the PDK's pad and SRAM models, synthesis or a layout. It proves the logic, not the timing or the manufactured chip. The gate-level simulation after LibreLane, which the template runs with make sim-gl, is the next check.

Alat yang Diperlukan

1

CC0 Domain Publik

Cetak biru ini dirilis di bawah CC0. Anda bebas untuk menyalin, memodifikasi, mendistribusikan, dan menggunakan karya ini untuk tujuan apa pun, termasuk komersial, tanpa meminta izin.

Dukung Maker dengan membeli produk melalui Cetak Biru mereka di mana mereka mendapatkan Komisi Pembuat yang ditetapkan oleh Penjual, atau buat iterasi baru dari Cetak Biru ini dan masukkan sebagai koneksi di milik Anda sendiri.

Diskusi

(0)

Masuk untuk bergabung dengan diskusi

Memuat komentar...