v0.1.5: fix bump_version.py BOM/encoding/CRLF, fix double banner on power-up
This commit is contained in:
@@ -1,61 +1,43 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Increment the PATCH component of version.txt and update banner_char in top.v."""
|
||||
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
VERSION_FILE = Path("version.txt")
|
||||
TOP_V_FILE = Path("top.v")
|
||||
|
||||
# Fixed 9-character field in banner_char; indices 343-351.
|
||||
VERSION_FIELD_LEN = 9
|
||||
VERSION_START_IDX = 343
|
||||
|
||||
|
||||
def read_version():
|
||||
if not VERSION_FILE.exists():
|
||||
sys.exit(f"Error: {VERSION_FILE} not found")
|
||||
return VERSION_FILE.read_text().strip()
|
||||
|
||||
|
||||
def bump_patch(version):
|
||||
parts = version.split(".")
|
||||
if len(parts) != 3 or not all(p.isdigit() for p in parts):
|
||||
sys.exit(f"Error: unexpected version format '{version}'")
|
||||
major, minor, patch = int(parts[0]), int(parts[1]), int(parts[2])
|
||||
return f"{major}.{minor}.{patch + 1}"
|
||||
|
||||
|
||||
def update_top_v(new_version):
|
||||
if not TOP_V_FILE.exists():
|
||||
sys.exit(f"Error: {TOP_V_FILE} not found")
|
||||
|
||||
padded = new_version.ljust(VERSION_FIELD_LEN)
|
||||
if len(padded) > VERSION_FIELD_LEN:
|
||||
sys.exit(f"Error: version '{new_version}' exceeds {VERSION_FIELD_LEN} characters")
|
||||
|
||||
content = TOP_V_FILE.read_text()
|
||||
|
||||
for offset, ch in enumerate(padded):
|
||||
idx = VERSION_START_IDX + offset
|
||||
pattern = rf"(9'd{idx}:\s+banner_char\s*=\s*)[^\n]+;"
|
||||
replacement = rf'\g<1>"{ch}";'
|
||||
new_content = re.sub(pattern, replacement, content)
|
||||
if new_content == content:
|
||||
sys.exit(f"Error: could not find banner_char entry for index {idx} in {TOP_V_FILE}")
|
||||
content = new_content
|
||||
|
||||
TOP_V_FILE.write_text(content)
|
||||
|
||||
|
||||
def main():
|
||||
old_version = read_version()
|
||||
new_version = bump_patch(old_version)
|
||||
update_top_v(new_version)
|
||||
VERSION_FILE.write_text(new_version + "\n")
|
||||
print(f"Version bumped to {new_version}")
|
||||
if not VERSION_FILE.exists():
|
||||
sys.exit("Error: version.txt not found")
|
||||
if not TOP_V_FILE.exists():
|
||||
sys.exit("Error: top.v not found")
|
||||
|
||||
old_version = VERSION_FILE.read_text(encoding='utf-8').strip()
|
||||
parts = old_version.split(".")
|
||||
if len(parts) != 3 or not all(p.isdigit() for p in parts):
|
||||
sys.exit(f"Error: unexpected version format '{old_version}'")
|
||||
new_version = f"{parts[0]}.{parts[1]}.{int(parts[2])+1}"
|
||||
padded = new_version.ljust(VERSION_FIELD_LEN)
|
||||
|
||||
content = TOP_V_FILE.read_text(encoding='utf-8-sig', newline='\n')
|
||||
|
||||
m = re.search(r'version chars at indices (\d+)', content)
|
||||
if not m:
|
||||
sys.exit("Error: could not find 'version chars at indices' comment in top.v")
|
||||
start_idx = int(m.group(1))
|
||||
|
||||
for offset, ch in enumerate(padded):
|
||||
idx = start_idx + offset
|
||||
pattern = rf"(9'd{idx}:\s+banner_char\s*=\s*)[^\n]+;"
|
||||
new_content, n = re.subn(pattern, lambda mo, c=ch: mo.group(1) + f'"{c}";', content)
|
||||
if n == 0:
|
||||
sys.exit(f"Error: could not find banner_char entry for index {idx}")
|
||||
content = new_content
|
||||
|
||||
TOP_V_FILE.write_text(content, encoding='utf-8', newline='\n')
|
||||
VERSION_FILE.write_text(new_version + "\n", encoding='utf-8')
|
||||
print(f"Version bumped to {new_version}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
126
top.v
126
top.v
@@ -6,31 +6,40 @@
|
||||
// clk_50mhz - onboard oscillator
|
||||
// rx_clk - DS90CF386 RxCLKOUT (~72 MHz pixel clock)
|
||||
// de - DS90CF386 RxOUT24
|
||||
// vsync - DS90CF386 RxOUT25 (currently unused, brought in for future use)
|
||||
// hsync - DS90CF386 RxOUT26 (currently unused, brought in for future use)
|
||||
// vsync - DS90CF386 RxOUT25 (unused, kept for pin assignment)
|
||||
// hsync - DS90CF386 RxOUT26 (unused, kept for pin assignment)
|
||||
// cntl - DS90CF386 RxOUT27 (unused, kept for pin assignment)
|
||||
// red[7:0] - DS90CF386 red channel
|
||||
// green[7:0]- DS90CF386 green channel
|
||||
// blue[7:0] - DS90CF386 blue channel
|
||||
// rst_n_pin - optional external reset (tie high if not used)
|
||||
//
|
||||
// Output:
|
||||
// uart_tx_pin - to CH340 RX
|
||||
//
|
||||
// Frame reports:
|
||||
// - On a clean frame, sends "OK\n" once every HEARTBEAT_FRAMES frames.
|
||||
// - On a clean frame, sends "OK\r\n" once every HEARTBEAT_FRAMES frames.
|
||||
// - On an anomalous frame, immediately sends
|
||||
// "ERR lines=NNNN width=NNNN\n"
|
||||
// where NNNN are zero-padded 4-digit decimals.
|
||||
// "ERR lines=NNNN width=NNNN R=RR G=GG B=BB\r\n"
|
||||
// where NNNN are zero-padded 4-digit decimals and RR/GG/BB are
|
||||
// 2-digit uppercase hex of the first pixel of the anomalous frame.
|
||||
|
||||
module top (
|
||||
input wire clk_50mhz,
|
||||
input wire rx_clk,
|
||||
input wire de,
|
||||
input wire vsync,
|
||||
input wire hsync,
|
||||
input wire rst_n_pin,
|
||||
output wire uart_tx_pin
|
||||
input wire clk_50mhz,
|
||||
input wire rx_clk,
|
||||
input wire de,
|
||||
input wire vsync,
|
||||
input wire hsync,
|
||||
input wire cntl,
|
||||
input wire [7:0] red,
|
||||
input wire [7:0] green,
|
||||
input wire [7:0] blue,
|
||||
input wire rst_n_pin,
|
||||
output wire uart_tx_pin
|
||||
);
|
||||
|
||||
// unused for now — keep ports alive so pin assignments stick
|
||||
wire _unused = &{1'b0, vsync, hsync, 1'b0};
|
||||
// keep undriven ports alive so pin assignments stick
|
||||
wire _unused = &{1'b0, vsync, hsync, cntl, 1'b0};
|
||||
|
||||
localparam integer HEARTBEAT_FRAMES = 60;
|
||||
|
||||
@@ -65,6 +74,9 @@ module top (
|
||||
wire [15:0] lines_pix;
|
||||
wire [15:0] width_pix;
|
||||
wire anomaly_pix;
|
||||
wire [7:0] first_r_pix;
|
||||
wire [7:0] first_g_pix;
|
||||
wire [7:0] first_b_pix;
|
||||
|
||||
de_monitor #(
|
||||
.EXPECTED_LINES (16'd800),
|
||||
@@ -74,10 +86,16 @@ module top (
|
||||
.pix_clk (rx_clk),
|
||||
.rst_n (rst_n_pix),
|
||||
.de (de),
|
||||
.red (red),
|
||||
.green (green),
|
||||
.blue (blue),
|
||||
.frame_done (frame_done_pix),
|
||||
.lines_o (lines_pix),
|
||||
.width_o (width_pix),
|
||||
.anomaly_o (anomaly_pix)
|
||||
.anomaly_o (anomaly_pix),
|
||||
.first_r (first_r_pix),
|
||||
.first_g (first_g_pix),
|
||||
.first_b (first_b_pix)
|
||||
);
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
@@ -87,8 +105,11 @@ module top (
|
||||
reg [15:0] width_lat;
|
||||
reg anomaly_lat;
|
||||
reg heartbeat_lat;
|
||||
reg [7:0] fr_lat;
|
||||
reg [7:0] fg_lat;
|
||||
reg [7:0] fb_lat;
|
||||
reg req_tog;
|
||||
reg [7:0] hb_count; // up to 255 — plenty for HEARTBEAT_FRAMES=60
|
||||
reg [7:0] hb_count; // up to 255 — plenty for HEARTBEAT_FRAMES=60
|
||||
|
||||
always @(posedge rx_clk or negedge rst_n_pix) begin
|
||||
if (!rst_n_pix) begin
|
||||
@@ -96,12 +117,18 @@ module top (
|
||||
width_lat <= 16'd0;
|
||||
anomaly_lat <= 1'b0;
|
||||
heartbeat_lat <= 1'b0;
|
||||
fr_lat <= 8'd0;
|
||||
fg_lat <= 8'd0;
|
||||
fb_lat <= 8'd0;
|
||||
req_tog <= 1'b0;
|
||||
hb_count <= 8'd0;
|
||||
end else if (frame_done_pix) begin
|
||||
lines_lat <= lines_pix;
|
||||
width_lat <= width_pix;
|
||||
anomaly_lat <= anomaly_pix;
|
||||
fr_lat <= first_r_pix;
|
||||
fg_lat <= first_g_pix;
|
||||
fb_lat <= first_b_pix;
|
||||
|
||||
if (hb_count == HEARTBEAT_FRAMES - 1) begin
|
||||
hb_count <= 8'd0;
|
||||
@@ -131,8 +158,11 @@ module top (
|
||||
reg [15:0] width_u;
|
||||
reg anomaly_u;
|
||||
reg heartbeat_u;
|
||||
reg [7:0] fr_u;
|
||||
reg [7:0] fg_u;
|
||||
reg [7:0] fb_u;
|
||||
|
||||
// BCD digit registers — computed in F_CONVERT before transmission
|
||||
// BCD digit registers — computed in F_CONVERT before transmission
|
||||
reg [3:0] L0_r, L1_r, L2_r, L3_r;
|
||||
reg [3:0] W0_r, W1_r, W2_r, W3_r;
|
||||
reg [1:0] conv_step;
|
||||
@@ -140,18 +170,24 @@ module top (
|
||||
|
||||
// Sample latched fields when req_edge fires. Data is stable in
|
||||
// pix domain until the next frame_done (~16 ms), far longer than we
|
||||
// need for the handful of µs of UART setup.
|
||||
// need for the handful of µs of UART setup.
|
||||
always @(posedge clk_uart or negedge rst_n_uart) begin
|
||||
if (!rst_n_uart) begin
|
||||
lines_u <= 16'd0;
|
||||
width_u <= 16'd0;
|
||||
anomaly_u <= 1'b0;
|
||||
heartbeat_u <= 1'b0;
|
||||
fr_u <= 8'd0;
|
||||
fg_u <= 8'd0;
|
||||
fb_u <= 8'd0;
|
||||
end else if (req_edge) begin
|
||||
lines_u <= lines_lat;
|
||||
width_u <= width_lat;
|
||||
anomaly_u <= anomaly_lat;
|
||||
heartbeat_u <= heartbeat_lat;
|
||||
fr_u <= fr_lat;
|
||||
fg_u <= fg_lat;
|
||||
fb_u <= fb_lat;
|
||||
end
|
||||
end
|
||||
|
||||
@@ -159,8 +195,7 @@ module top (
|
||||
localparam [2:0] F_IDLE = 3'd0,
|
||||
F_CONVERT = 3'd1,
|
||||
F_LOAD = 3'd2,
|
||||
F_WAIT = 3'd3,
|
||||
F_BANNER = 3'd4;
|
||||
F_WAIT = 3'd3;
|
||||
|
||||
// Banner: ESC[2J ESC[H ESC[33m + 2x51-dash separators + info lines + ESC[0m
|
||||
localparam [8:0] BANNER_LEN = 9'd411;
|
||||
@@ -174,7 +209,15 @@ module top (
|
||||
reg [7:0] tx_byte;
|
||||
wire tx_busy;
|
||||
|
||||
// ERR layout: ESC[31m + "ERR lines=LLLL width=WWWW" + ESC[0m + \r\n (36 bytes)
|
||||
// Nibble (0-15) to uppercase ASCII hex digit
|
||||
function [7:0] hex_char(input [3:0] n);
|
||||
hex_char = (n <= 4'd9) ? ("0" + {4'h0, n}) : ("A" - 8'd10 + {4'h0, n});
|
||||
endfunction
|
||||
|
||||
// ERR layout (51 bytes):
|
||||
// ESC[31m(5) + "ERR lines="(10) + LLLL(4) + " width="(7) + WWWW(4)
|
||||
// + " R="(3) + RR(2) + " G="(3) + GG(2) + " B="(3) + BB(2)
|
||||
// + ESC[0m(4) + \r\n(2)
|
||||
function [7:0] err_char(input [7:0] i);
|
||||
case (i)
|
||||
8'd0: err_char = 8'h1B;
|
||||
@@ -207,12 +250,27 @@ module top (
|
||||
8'd27: err_char = "0" + W1_r;
|
||||
8'd28: err_char = "0" + W2_r;
|
||||
8'd29: err_char = "0" + W3_r;
|
||||
8'd30: err_char = 8'h1B;
|
||||
8'd31: err_char = "[";
|
||||
8'd32: err_char = "0";
|
||||
8'd33: err_char = "m";
|
||||
8'd34: err_char = 8'h0D;
|
||||
8'd35: err_char = 8'h0A;
|
||||
8'd30: err_char = " ";
|
||||
8'd31: err_char = "R";
|
||||
8'd32: err_char = "=";
|
||||
8'd33: err_char = hex_char(fr_u[7:4]);
|
||||
8'd34: err_char = hex_char(fr_u[3:0]);
|
||||
8'd35: err_char = " ";
|
||||
8'd36: err_char = "G";
|
||||
8'd37: err_char = "=";
|
||||
8'd38: err_char = hex_char(fg_u[7:4]);
|
||||
8'd39: err_char = hex_char(fg_u[3:0]);
|
||||
8'd40: err_char = " ";
|
||||
8'd41: err_char = "B";
|
||||
8'd42: err_char = "=";
|
||||
8'd43: err_char = hex_char(fb_u[7:4]);
|
||||
8'd44: err_char = hex_char(fb_u[3:0]);
|
||||
8'd45: err_char = 8'h1B;
|
||||
8'd46: err_char = "[";
|
||||
8'd47: err_char = "0";
|
||||
8'd48: err_char = "m";
|
||||
8'd49: err_char = 8'h0D;
|
||||
8'd50: err_char = 8'h0A;
|
||||
default: err_char = 8'h00;
|
||||
endcase
|
||||
endfunction
|
||||
@@ -576,7 +634,7 @@ module top (
|
||||
9'd344: banner_char = ".";
|
||||
9'd345: banner_char = "1";
|
||||
9'd346: banner_char = ".";
|
||||
9'd347: banner_char = "0";
|
||||
9'd347: banner_char = "5";
|
||||
9'd348: banner_char = " ";
|
||||
9'd349: banner_char = " ";
|
||||
9'd350: banner_char = " ";
|
||||
@@ -605,7 +663,7 @@ module top (
|
||||
|
||||
always @(posedge clk_uart or negedge rst_n_uart) begin
|
||||
if (!rst_n_uart) begin
|
||||
fstate <= F_BANNER;
|
||||
fstate <= F_LOAD;
|
||||
idx <= 9'd0;
|
||||
msg_len <= BANNER_LEN;
|
||||
is_banner <= 1'b1;
|
||||
@@ -620,16 +678,12 @@ module top (
|
||||
end else begin
|
||||
tx_start <= 1'b0;
|
||||
case (fstate)
|
||||
F_BANNER: begin
|
||||
// idx/msg_len/is_banner already set in reset block; kick off load
|
||||
fstate <= F_LOAD;
|
||||
end
|
||||
F_IDLE: begin
|
||||
is_banner <= 1'b0;
|
||||
if (req_edge_q && (anomaly_u || heartbeat_u)) begin
|
||||
if (req_edge_q && (anomaly_u || heartbeat_u) && !is_banner) begin
|
||||
idx <= 9'd0;
|
||||
is_err_msg <= anomaly_u;
|
||||
msg_len <= anomaly_u ? 9'd36 : 9'd13;
|
||||
msg_len <= anomaly_u ? 9'd51 : 9'd13;
|
||||
l_rem <= lines_u;
|
||||
w_rem <= width_u;
|
||||
conv_step <= 2'd0;
|
||||
@@ -705,7 +759,7 @@ module top (
|
||||
else if (w_rem >= 16'd10) begin W2_r <= 4'd1; w_rem <= w_rem - 16'd10; end
|
||||
else W2_r <= 4'd0;
|
||||
end
|
||||
2'd3: begin // units — remainder is 0-9 by construction
|
||||
2'd3: begin // units — remainder is 0-9 by construction
|
||||
L3_r <= l_rem[3:0];
|
||||
W3_r <= w_rem[3:0];
|
||||
fstate <= F_LOAD;
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.1.0
|
||||
0.1.5
|
||||
|
||||
Reference in New Issue
Block a user