本节摘要:移位寄存器让数据在寄存器里逐位流动,是串并转换与流水线的基础构件。本节实现串入并出与并入串出两个方向的通用模块,讲清移位方向与拼接写法的对应关系,并给出序列检测器这个经典应用。
把 N 个 D 触发器级联——前一级的 Q 接后一级的 D,共用时钟——每个时钟沿数据整体挪一格,这就是移位寄存器。数据的进口和出口位置决定了它的身份:串入并出(数据从一端一位位进来,凑齐 N 位后并行输出)用于串行接收;并入串出(并行数据一次装入,再一位位送出)用于串行发送。计算器原型里它有实际戏份:按键扫描如果走串行接口,收发全靠它。
串入并出的核心只有一行:
library ieee; use ieee.std_logic_1164.all; entity sipo is generic (WIDTH : positive := 8); port ( clk : in std_logic; rst : in std_logic; ser_in : in std_logic; -- 串行输入 par_out : out std_logic_vector(WIDTH-1 downto 0) ); end entity sipo; architecture rtl of sipo is signal reg : std_logic_vector(WIDTH-1 downto 0) := (others => '0'); begin process (clk) begin if rising_edge(clk) then if rst = '1' then reg <= (others => '0'); else reg <= reg(WIDTH-2 downto 0) & ser_in; -- 左移:整体左挪,右端进新位 end if; end if; end process; par_out <= reg; end architecture rtl;
移位的本质是拼接:reg 的低 WIDTH-1 位拼上新输入,整体赋回 reg。方向口诀:& ser_in 在右边是左移,最先进入的位最终到达 reg 的最高位——先发的位在高位,这与串行协议"先发高位或先发低位"的约定直接相关,接口设计时必须写进注释。
并入串出方向相反,还要解决"何时装载":
entity piso is generic (WIDTH : positive := 8); port ( clk, rst, load : in std_logic; par_in : in std_logic_vector(WIDTH-1 downto 0); ser_out : out std_logic ); end entity piso; architecture rtl of piso is signal reg : std_logic_vector(WIDTH-1 downto 0) := (others => '0'); begin process (clk) begin if rising_edge(clk) then if rst = '1' then reg <= (others => '0'); elsif load = '1' then reg <= par_in; -- 并行装载 else reg <= reg(WIDTH-2 downto 0) & '0'; -- 左移,最高位将被挤出去 end if; end if; end process; ser_out <= reg(WIDTH-1); -- 高位先出 end architecture rtl;
ser_out 直接取 reg 的最高位——每次移位,最高位被挤出去,正是"发送"。装载优先于移位,if-elsif 顺序再次兑现优先级规则。

移位寄存器最常见的用法是滑动窗口:让最近的 N 位一直住在寄存器里,随时与目标模式比对。检测串行流里的序列 1-0-1-1:
architecture rtl of detect1011 is signal window : std_logic_vector(3 downto 0) := (others => '0'); begin process (clk) begin if rising_edge(clk) then window <= window(2 downto 0) & ser_in; -- 滑窗更新 end if; end process; hit <= '1' when window = "1011" else '0'; -- 组合比较 end architecture rtl;
每个时钟沿窗口左滑一位,比较器持续盯着窗口内容——两行核心逻辑完成了"连续监视串行流"的任务。这个结构与第 5.4 节的状态机检测器功能相同,对比二者是很好的练习:滑窗写法直白、多占触发器;状态机省资源、但要画状态图。工程上位宽小用滑窗、位宽大或流程复杂用状态机。
测试台发一组已知序列,比如 1、1、0、1、1、0、1、1,同时监视 par_out 与 hit:逐拍记录窗口内容,验证每次移位后各位的相对位置不变(整体平移);hit 必须恰好在窗口内容等于 1-0-1-1 的拍拉高。串行模块的波形验证技巧是把窗口内容拼成一个信号显示(很多波形工具支持把向量按二进制显示),肉眼直接读"1-0-1-1"比逐位核对快得多。
⚠️ 常见坑:移位方向与协议要求不一致——发送方最高位先出、接收方却按最低位先收,数据全镜像。串行接口设计的第一件事是把"谁先谁后"写进两端的注释,测试台里用回环(发什么收什么)先自证。
💡 关键直觉:移位寄存器是"传送带上的格子"。每拍全体向前挪一格,新货从一端上、旧货从另一端下;滑窗是"永远盯着传送带最后上的几件"。
reg(W-2 downto 0) & 新位,拼接位置决定方向;场景 1:串行-并行转换
把 N 位串行数据流转换成 N 位并行总线(典型应用:SPI 接收器):
process(clock) begin if rising_edge(clock) then if shift_en = '1' then shift_reg <= shift_reg(6 downto 0) & serial_in; end if; end if; end process; parallel_out <= shift_reg;
场景 2:延迟线
把信号延迟 N 个时钟周期(典型应用:流水线、对齐信号):
process(clock) begin if rising_edge(clock) then if ce = '1' then delay_pipe(0) <= data_in; for i in 1 to N-1 loop delay_pipe(i) <= delay_pipe(i-1); end loop; end if; end if; end process; data_out <= delay_pipe(N-1);
在 FPGA 里,移位寄存器会被推断为 SRL(移位寄存器查找表,Xilinx)或 ALTSHIFT_TAPS(Intel)。16 位以内的移位用 SRL 很高效,超过则综合为多个 SRL 串联或触发器链。Vivado 报告会显示用了多少 SRL。
"for generate" 实现移位寄存器:
gen: for i in 0 to N-1 generate dffs: dff port map(clk => clock, d => data_in, q => sig(i)); end generate;
这种写法每个 DFF 占一个 slice 寄存器,但 SRL 模式下 1 个 slice 就能存 16 位。资源浪费 16 倍。