“硬核”代码重构

在学习编程的路上,相信大家这几个词一定不少听,什么 面相对象、封装继承多态、内功心法21种设计模式 等等 。但是却很少用到,或者说用到的都是被动使用。大牛们在写代码前早就构思好了,接口,基类等等。自己写代码的时候,很少有把面向对象想的面很全,很容易在遇上不够优秀的代码,这时候就需要重构了。

但是我们却很少去重构,可能原因有很多,比如很重要的一点:不想改出Bug;不想增加工作量(我是要5点半下班的男人,女朋友还在等我做饭);时间很紧,先实现功能先;代码是82年的不敢动!!!

其实重构可以写出更健壮的代码、减少后面的工作量、让开发者更好阅读。看了很多重构的文章,发现很多是一些基本的,命名规范或者拆函数什么的。这篇文章写下我重构的一些思路和重构之前代码对比。好了,废话不多说,上项目。威武、威武、威武........

简单介绍一下项目,项目就是一个客户端小工具用来审核编写递交的说明是否规范。这里说下,面向对象是一种思想,和语言无关。只要是面向对象语言,无论你是C#、Java、TypeScript、Python,都是可以用这种思想重构的。

上图就是小工具了一个部分截图,有若干个栏,每个栏都是填写一些对应的修改内容,在审核校验时,会检查写的内容是否符合标准。

前辈已经完成一些栏的校验,我的任务是完成剩下栏:写正则表达式,然后在不标准的时候提示就好了,是不是觉得根本没有必要重构,依葫芦画瓢就好了在我拿到代码的时候,是这样的代码:

// 下面变量是和界面绑定的变量,RaisePropertyChanged作用在变量改变的时候通知前端重新渲染
// 不熟悉C#代码的,只用知道这些变量就是和前台绑定的就是了

    private string _editDescription;
    /// <summary>
    /// 修改说明内容
    /// </summary>
    public string EditDescription
    {
        get { return _editDescription; }
        set
        {
            _editDescription = value;
            RaisePropertyChanged(() => EditDescription);
        }
    }

    private string _editDescriptionRules;
    /// <summary>
    /// 修改说明校验规则
    /// </summary>
    public string EditDescriptionRules
    {
        get { return _editDescriptionRules; }
        set
        {
            _editDescriptionRules = value;
            RaisePropertyChanged(() => EditDescriptionRules);
        }
    }

    private bool _editDescriptionHasValidationError;
    /// <summary>
    /// 修改说明校验标志
    /// </summary>
    public bool EditDescriptionHasValidationError
    {
        get { return _editDescriptionHasValidationError; }
        set
        {               
            _editDescriptionHasValidationError = value;
            RaisePropertyChanged(() => EditDescriptionHasValidationError);
        }
    }

    private string _integratedNote;
    /// <summary>
    /// 集成注意内容
    /// </summary>
    public string IntegratedNote
    {
        get { return _integratedNote; }
        set
        {
            _integratedNote = value;
            RaisePropertyChanged(() => IntegratedNote);
        }
    }

    private string _integratedNoteRules;
    /// <summary>
    /// 集成注意规则
    /// </summary>
    public string IntegratedNoteRules
    {
        get { return _integratedNoteRules; }
        set
        {
            _integratedNoteRules = value;
            RaisePropertyChanged(() => IntegratedNoteRules);
        }
    }

    private bool _integratedNoteHasValidationError;
    /// <summary>
    /// 集成注意校验标志
    /// </summary>
    public bool IntegratedNoteHasValidationError
    {
        get { return _integratedNoteHasValidationError; }
        set
        {
            _integratedNoteHasValidationError = value;
            RaisePropertyChanged(() => IntegratedNoteHasValidationError);
        }
    }
     
// 这里随便举了两栏的变量,后面还有若干栏。
复制代码

依葫芦画瓢以后呢,我发现原来是这样的。每一栏用了单独三个变量直接去绑定:编写的内容、是否标准的标志、不标准提示语。我是一个懒人啊,在我画了两个瓢以后,就很烦(一直在那复制变量,在那改来改去),这些个变量都是差不多一个意思。为啥让我重复在复制,修改呢?

明显每栏有相同项啊,对不对,一个是内容,一个状态,一个是错误提示语啊,咆哮!!!

这时候,我想起了书本上的一句话: "早重构,常重构"

我已经安奈不住我那颗懒惰的心里,因为下面还有“狠多狠多”栏,每一行有三个类似的变量,我这依葫芦画瓢,这个星期加班,就在复制粘贴去了。我是要上进,要每天进步的人,不能这样!

我为啥不能将这相同的共性抽象出来呢?是不咯,这个时候,我想起了《葵花宝典》的第一页的第一句:“万物皆对象”,于是本能的告诉我,每一栏看做一个对象,只是每栏的值不一样。然后我就写了一个”鸡肋“(基类),代码如下:

    /// <summary>
    /// 栏 基类
    /// </summary>
    public class Base: ObservableObject
    {
        private string _content;
        /// <summary>
        /// 内容
        /// </summary>
        public virtual string Content
        {
            get { return _content; }
            set
            {
                _content = value;
                RaisePropertyChanged(() => Content);
            }
        }

        private string _errorTip;
        /// <summary>
        /// 错误提示
        /// </summary>
        public virtual string ErrorTip
        {
            get { return _errorTip; }
            set
            {
                _errorTip = value;
                RaisePropertyChanged(() => ErrorTip);
            }
        }

        private bool _isError;
        /// <summary>
        /// 是否错误
        /// </summary>
        public virtual bool IsError
        {
            get { return _isError; }
            set
            {
                _isError = value;
                RaisePropertyChanged(() => ErrorTip);
            }
        }
    }
复制代码

virtual是为了让子类能够重写get和set(如果有需求的话,为后面扩展做准备),然后字段从3个变到了1个了。

/// <summary>
/// 修改说明栏
/// </summary>
public class EditDescription : Base { }		
private EditDescription _editDescriptions;
/// <summary>
/// 修改说栏绑定变量
/// </summary>
public EditDescription EditDescriptions
{
    get { return _editDescriptions; }
    set
        {
            _editDescriptions = value;
            RaisePropertyChanged(() => EditDescriptions);
        }
}
        
// 其他的一样,我就不多写了
复制代码

那,我们来算一下账,原先的变量,每一栏有3个变量,一个变量有6行代码的话,假如我这个有100栏,就是:

重构前: 100(栏)x3x6 = 1800 行代码 (阿西吧!!!)。

重构后: 100(栏)x1x6 = 600 行代码 。

小学算数: 1800 - 600 = 1200 (1200行,你说干点啥不好啊)

秀儿们算下,你花这么多时间,在那复制粘贴,不敢去动前辈们的代码,还是勇敢一点呢?

然后是不是感觉到一个继承就简单了很多呢,这只是一个面向对象的简单运用,就让我们少写了这么多代码。

前辈和我说,写代码就像练武功,就像你会了“九阳神功”或者"吸星大法",学其他武功看一眼就会。也就是说,当你理解了面向对象以后呢,你自然而然的就会写出很精简的代码了。阿西吧,又扯远了。

变量好了,抬头一看函数,我的脸便有点抽搐,啊!!这函数有毒!函数是这样的:

  // 此处函数用来设置每一栏报错时边框变红
    private void SetValidateFlag()
    {
        // 绑定的实体类判断状态
        if (tsEntity.EditDescriptionHasValidationError)
        {
            // 控件边框改颜色
            EditDescriptionHsExpander.BorderThickness = new Thickness(1);
            EditDescriptionHsExpander.BorderBrush = _readBrush;
        }

        if (tsEntity.TestSuggestionHasValidationError)
        {
            TestSuggestionHsExpander.BorderThickness = new Thickness(1);
            TestSuggestionHsExpander.BorderBrush = _readBrush;
        }

        if (tsEntity.IntegratedNoteHasValidationError)
        {
            IntegratedNoteHsExpander.BorderThickness = new Thickness(1);
            IntegratedNoteHsExpander.BorderBrush = _readBrush;
        }
        // 此处省略一万个if,每个栏都有一个if
    }
复制代码

然后大家懂的嘛,在我改了两栏以后,我又耐不住性子了。再一看,感觉似曾相识燕归来的感觉啊!有没有,每个if中都有三个类似的东西。我那个心啊,又忍不住悸动了起来,像是等初恋的感觉,想她来,来了又不知道要干哈。然后我发现其实if中判断的就是每栏的状态,括号里面是对控件栏的设置。然后想嘛,能不能搞个类似循环的东西,只要写一个for就好了呢。

思考以后,是这样的,我把控件也变成了栏的一个属性了,这样if判断里面就都是判断一个类了。代码就变成了这样:

    public class Base: ObservableObject
    {
        /// <summary>
        /// 模块控件(新增)
        /// </summary>
        public object Control { get; set; }
        
        //其他的和上面的Base一样
    }
复制代码

然后 SetValidateFlag() 函数就变成了这样:

    /// <summary>
    /// 设置校验界面效果
    /// </summary>
    private void SetValidateFlag()
    {
        // listBase 所有栏实体集合
        foreach (var item in listBase)
        {
            if (item.IsError)
            {
                var control = item.Control as HsExpander;
                if (control == null)
                    continue;
                // 将控件的边框变成红色
                control.BorderThickness = new Thickness(1);
                control.BorderBrush = _readBrush;
            }
        }
    }
复制代码

好了嘞,好好的一个if的葫芦瓢给咱给整没了。这时候我们来算算我们这个重构,省了多少事。老样子:

原先的一个if算5行代码,我这个有100栏,就是:

重构前: 100(栏)x 5 = 500 行代码 (全是if啊)。

重构后: 我数了一下,没有错的话应该是:14行代码 。

重构一下以后,变量得到了减少且对外统一。感觉一颗心得到了小小满足,感觉灵魂得到了升华,不知道是自己太容易满足,还是代码世界里给我的成就感。感觉“乾坤大挪移”瞬间上了两层。

这就是我在写代码的时候一个小小的重构思路,希望能够帮助到大家一点。然后这个是我的Github,如果有帮助到大家的项目,可以给我点个star, 小生在这边谢谢啦!!!

<think>好的,用户现在问的是关于远程更新Verilog代码的示例。首先,我需要明确用户的需求。他们可能是在进行FPGA开发,想要在不物理接触设备的情况下更新代码,比如通过无线网络或者其他通信方式。这可能涉及到FPGA的远程配置,所以需要了解FPGA的配置机制,尤其是动态部分重配置或者远程更新比特流的流程。 接下来,我要回想之前的对话历史。之前用户询问了为什么.bit文件打开是乱码,我解释了二进制文件的特性以及如何正确使用工具进行验证和解析。现在的问题是一个延伸,用户可能已经了解了.bit文件的基本知识,现在想进一步实现远程更新,这可能包括如何生成、传输和加载新的比特流文件。 用户可能需要的不仅仅是代码示例,而是整个流程的说明,包括硬件设计、通信协议的选择、安全验证等。他们可能没有考虑到远程更新的复杂性,比如需要引导加载程序(Bootloader)、双镜像备份、错误恢复机制等。因此,在回答时,我需要分步骤详细说明,并提供示例代码片段,同时强调关键点如通信协议、存储管理、安全性等。 需要确定用户使用的FPGA型号,比如Xilinx的Zynq系列可能支持通过处理器系统进行远程更新,而纯FPGA可能需要外部控制器。这里可能需要假设用户使用的是常见的平台,如Zynq,因为其结合了处理器和FPGA,适合远程更新场景。 另外,用户可能对Verilog代码的具体实现感兴趣,但远程更新通常涉及到更高层的系统设计,包括处理器端的软件和FPGA的逻辑协同工作。因此,示例可能需要包括硬件描述和软件脚本的结合,比如使用UART或以太网通信,将新的比特流存储到Flash,然后触发重配置。 还需要考虑可能的误区,比如直接通过Verilog实现通信协议可能不够高效,通常需要借助处理器或软核(如MicroBlaze)来处理协议栈。因此,在示例中可能需要分模块设计,包括通信接口模块、存储控制模块和配置管理模块。 最后,要确保回答结构清晰,逐步引导用户从设计思路到具体实现,并提供验证方法,确保他们能够理解并实施远程更新方案,同时提醒注意事项,如版本回退、看门狗机制防止更新失败导致设备变砖等。</think>在FPGA开发中,**远程更新Verilog代码**通常指通过通信接口(如UART、Ethernet、WiFi等)动态更新FPGA的比特流(`.bit`文件)。以下是具体实现思路及简化示例: --- ### **1. 核心设计原理** 远程更新的本质是**动态重构FPGA逻辑**,需实现以下模块: 1. **通信接口模块**:接收远程数据(新比特流) 2. **非易失存储控制器**:将比特流写入Flash/SD卡 3. **配置管理模块**:触发FPGA重配置(从存储加载新比特流) --- ### **2. 硬件架构示例(基于Xilinx Zynq)** ```verilog // 顶层模块:远程更新控制器 module remote_update ( input wire clk, // 系统时钟 input wire rst_n, // 复位信号 input wire uart_rx, // UART接收数据 output wire flash_cs, // Flash片选 output wire flash_sclk, // Flash时钟 inout wire flash_io0, // Flash数据线 output wire reconfigure // 重配置触发信号 ); //--- 子模块实例化 --- // UART接收解析模块 uart_receiver uart_rx_inst ( .clk(clk), .rst_n(rst_n), .rx_data(uart_rx), .data_valid(data_valid), .bitstream_data(bitstream_data) ); // SPI Flash控制器 spi_flash_controller flash_ctrl ( .clk(clk), .rst_n(rst_n), .write_en(data_valid), .data_in(bitstream_data), .cs(flash_cs), .sclk(flash_sclk), .io0(flash_io0) ); // 重配置状态机 reconfig_FSM fsm_inst ( .clk(clk), .flash_ready(flash_ready), .trigger(reconfigure) ); endmodule ``` --- ### **3. 关键代码模块详解** #### **(1) UART接收模块(简化版)** ```verilog module uart_receiver ( input wire clk, input wire rst_n, input wire rx_data, output reg data_valid, output reg [7:0] bitstream_data ); // 状态定义:IDLE, START, DATA, STOP reg [2:0] state; reg [3:0] bit_count; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin state <= IDLE; data_valid <= 0; end else begin case (state) IDLE: if (!rx_data) begin // 检测起始位 state <= START; bit_count <= 0; end START: state <= DATA; DATA: if (bit_count < 8) begin bitstream_data[bit_count] <= rx_data; bit_count <= bit_count + 1; end else begin state <= STOP; data_valid <= 1; // 数据接收完成 end STOP: begin data_valid <= 0; state <= IDLE; end endcase end end endmodule ``` #### **(2) SPI Flash控制器(关键部分)** ```verilog module spi_flash_controller ( input wire clk, input wire rst_n, input wire write_en, input wire [7:0] data_in, output reg cs, output reg sclk, inout wire io0 ); reg [7:0] shift_reg; reg [2:0] bit_counter; // SPI时钟生成(分频) reg [3:0] clk_div; always @(posedge clk) begin clk_div <= clk_div + 1; sclk <= clk_div[3]; // 假设系统时钟100MHz → SPI时钟约12.5MHz end // 数据传输状态机 always @(posedge sclk or negedge rst_n) begin if (!rst_n) begin cs <= 1'b1; shift_reg <= 8'h00; end else if (write_en) begin cs <= 1'b0; shift_reg <= data_in; bit_counter <= 0; end else if (!cs) begin if (bit_counter < 7) begin shift_reg <= {shift_reg[6:0], 1'b0}; bit_counter <= bit_counter + 1; end else begin cs <= 1'b1; // 传输结束 end end end assign io0 = (cs) ? 1'bz : shift_reg[7]; endmodule ``` --- ### **4. 远程更新流程** 1. **接收新比特流** - 通过UART/Ethernet接收加密压缩的比特流文件 - 示例:使用`lwIP`协议栈处理TCP数据包 2. **写入非易失存储** - 将比特流写入QSPI Flash的**备用区域**(保留原版本) - 地址管理示例: ```c #define CURRENT_FW_ADDR 0x00000000 #define NEW_FW_ADDR 0x01000000 // 16MB偏移 ``` 3. **触发重配置** - 通过Zynq的Processor Configuration Access Port (PCAP) 启动重配置 - Linux端命令(若使用PetaLinux): ```bash echo 1 > /sys/class/fpga_manager/fpga0/firmware ``` --- ### **5. 安全增强设计** ```verilog // 在Verilog中添加校验模块 module fw_verifier ( input wire [31:0] crc32_received, input wire [31:0] crc32_calculated, output wire fw_valid ); // 使用预计算的CRC32校验 assign fw_valid = (crc32_received == crc32_calculated); // 可选:添加RSA验签逻辑 // 需要调用硬核乘法器或BRAM实现 endmodule ``` --- ### **6. 实际部署注意事项** 1. **双镜像备份** - 在Flash中保留两个比特流副本,通过`Golden Image`机制回退 2. **看门狗定时器** - 更新过程中启用硬件看门狗,超时自动复位 3. **通信协议优化** - 使用差分传输(如`rsync`算法)减少数据量 - 示例协议格式: ``` [Header 0x55AA][FW_Size(4B)][CRC32(4B)][Payload...] ``` --- ### **7. 验证方法** 1. **仿真测试** - 使用Verilog测试平台模拟远程数据输入 - 检查Flash写入地址是否正确 2. **板上调试** - 通过ChipScope监控重配置触发信号 - 测量Flash写入时序是否符合规格书 --- ### **总结** 远程更新FPGA代码需要**硬件逻辑 + 软件协议栈 + 安全机制**的协同设计。关键点在于: - 通信协议稳定性(重传/校验机制) - 存储管理(双镜像/坏块处理) - 容错设计(更新失败自动回滚) 实际项目中建议使用成熟方案如: - Xilinx的**Multiboot**技术 - 基于Zynq MPSoC的**Remote System Update (RSU)** - 开源框架**OpenOCD**的扩展实现
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值