// uuid256.v — reference implementation of README.md (UUID256, random layout) in Verilog-2005 (synthesizable modules).
//
//   Simulate with the testbench:  iverilog -o /tmp/uuid256.vvp uuid256.v uuid256_tb.v && vvp /tmp/uuid256.vvp +selftest
//                                 vvp /tmp/uuid256.vvp +gen=5            (print 5 ids; the testbench reads /dev/urandom)
//
// Bit numbering: id[255:0] is the 32-byte value with byte 0 (the most significant, first in text) in id[255:248],
// so byte k occupies id[255-8k : 248-8k].  Text vectors hold ASCII with the first character in the top byte, which is
// exactly how Verilog string literals are laid out ("abc" == 24'h616263), so `text == "0001…1e1f"` compares directly.
//
//   uuid256_apply     §4    raw → id with byte 12 high nibble := 4 and byte 16 top two bits := 10 (pure wiring)
//   uuid256_is_strict §6    id[159:156] == 4 && id[127:126] == 2'b10
//   uuid256_format    §3.1  id → 68 ASCII chars: 16-8-8-8-24 lowercase hex (combinational)
//   uuid256_parse     §6    text (68 canonical or 64 compact, any case) → id, err (0 ok · 1 length · 2 hyphen · 3 char · 4 version)
// Randomness (§5.2) is not a logic function: in silicon you feed uuid256_apply from a TRNG/DRBG block; in simulation
// the testbench reads the host OS CSPRNG (/dev/urandom).

`timescale 1ns/1ps

module uuid256_apply (input  wire [255:0] raw,
                      output wire [255:0] id);
    // bytes 0..11 | ver=4 | low nibble of byte 12 | bytes 13..15 | var=10 | low 6 bits of byte 16 | bytes 17..31
    assign id = { raw[255:160], 4'h4, raw[155:152], raw[151:128], 2'b10, raw[125:0] };
endmodule

module uuid256_is_strict (input  wire [255:0] id,
                          output wire         strict);
    assign strict = (id[159:156] == 4'h4) && (id[127:126] == 2'b10);
endmodule

module uuid256_format (input  wire [255:0]   id,
                       output reg  [68*8-1:0] text);   // text[543:536] = first char
    function [7:0] hexchar (input [3:0] n);
        hexchar = (n < 10) ? (8'h30 + n) : (8'h61 + n - 10);   // '0'..'9', 'a'..'f'
    endfunction
    integer i, o;
    always @* begin
        text = {68*8{1'b0}};
        o = 0;
        for (i = 0; i < 32; i = i + 1) begin
            text[543 - 8*o -: 8]     = hexchar(id[255 - 8*i -: 4]);   // high nibble
            text[543 - 8*(o+1) -: 8] = hexchar(id[251 - 8*i -: 4]);   // low nibble
            o = o + 2;
            if (i == 7 || i == 11 || i == 15 || i == 19) begin          // hyphen after hex digits 16, 24, 32, 40
                text[543 - 8*o -: 8] = 8'h2d;
                o = o + 1;
            end
        end
    end
endmodule

module uuid256_parse (input  wire [68*8-1:0] text,      // ASCII, first char in text[543:536]; unused tail ignored
                      input  wire [6:0]      len,       // 68 (canonical) or 64 (compact)
                      input  wire            strict,
                      output reg  [255:0]    id,
                      output reg  [2:0]      err);      // 0 ok, 1 length, 2 hyphen, 3 char, 4 version/variant
    function [4:0] hexval (input [7:0] c);              // {valid, nibble}
        if (c >= 8'h30 && c <= 8'h39)      hexval = {1'b1, c[3:0]};          // '0'..'9'
        else if (c >= 8'h61 && c <= 8'h66) hexval = {1'b1, 4'd10 + c[3:0] - 4'd1};   // 'a'..'f'
        else if (c >= 8'h41 && c <= 8'h46) hexval = {1'b1, 4'd10 + c[3:0] - 4'd1};   // 'A'..'F'
        else                               hexval = 5'b0;
    endfunction
    function [7:0] ch (input [68*8-1:0] t, input integer p); ch = t[543 - 8*p -: 8]; endfunction
    integer i, bi;
    reg hy;
    reg [4:0] hv;
    reg [255:0] tmp;
    always @* begin
        err = 3'd0; id = 256'd0; tmp = 256'd0; hy = 1'b0; bi = 0;
        if (len == 7'd68) begin
            hy = 1'b1;
            if (ch(text,16) != 8'h2d || ch(text,25) != 8'h2d || ch(text,34) != 8'h2d || ch(text,43) != 8'h2d) err = 3'd2;
        end else if (len != 7'd64) err = 3'd1;
        if (err == 0) begin
            for (i = 0; i < 68; i = i + 1) begin
                if (i < len && !(hy && (i == 16 || i == 25 || i == 34 || i == 43)) && err == 0) begin
                    hv = hexval(ch(text, i));
                    if (!hv[4]) err = 3'd3;
                    else begin
                        tmp[255 - 4*bi -: 4] = hv[3:0];
                        bi = bi + 1;
                    end
                end
            end
        end
        if (err == 0) begin
            id = tmp;
            if (strict && !((tmp[159:156] == 4'h4) && (tmp[127:126] == 2'b10))) err = 3'd4;
        end
    end
endmodule
