From 0a5f8363fe4e6b3c7d4f17fde61e00ab63e43bcb Mon Sep 17 00:00:00 2001
From: Uros Majstorovic <majstor@majstor.org>
Date: Mon, 21 Mar 2022 02:24:59 +0100
Subject: i2s stereo driver

---
 fw/fe310/eos/board.h      |   8 +--
 fw/fe310/eos/eos.c        |   4 +-
 fw/fe310/eos/eve/eve.c    |  10 +--
 fw/fe310/eos/i2s.c        |  52 +++++++++++----
 fw/fe310/eos/trap_entry.S | 163 +++++++++++++++++++++++++++-------------------
 5 files changed, 146 insertions(+), 91 deletions(-)

(limited to 'fw')

diff --git a/fw/fe310/eos/board.h b/fw/fe310/eos/board.h
index a695029..a18b58f 100644
--- a/fw/fe310/eos/board.h
+++ b/fw/fe310/eos/board.h
@@ -30,14 +30,14 @@
 #define I2S_PIN_SD_IN           13
 #define I2S_PIN_SD_OUT          12
 
-#define I2S_IRQ_WS_ID           (INT_PWM2_BASE + 0)
-#define I2S_IRQ_SD_ID           (INT_PWM2_BASE + 3)
+#define I2S_IRQ_WS_ID           (INT_PWM1_BASE + 0)
+#define I2S_IRQ_SD_ID           (INT_PWM1_BASE + 3)
 
 #define I2S_CTRL_ADDR_CK        PWM0_CTRL_ADDR
 #define I2S_CTRL_ADDR_WS_MIC    PWM1_CTRL_ADDR
 #define I2S_CTRL_ADDR_WS_SPK    PWM2_CTRL_ADDR
 
-#define I2S_IDLE_CYCLES         8
+#define I2S_IDLE_CYCLES         1
 
 #define CTP_PIN_INT             1
 #define CTP_PIN_RST             19
@@ -46,5 +46,5 @@
 
 #define EVE_GPIO_CAM            0
 #define EVE_GPIO_LCD_EN         1
-#define EVE_GPIO_GAIN_SEL       2
+#define EVE_GPIO_GAIN           2
 #define EVE_GPIO_HAPT           3
diff --git a/fw/fe310/eos/eos.c b/fw/fe310/eos/eos.c
index bf0f0da..b52ed93 100644
--- a/fw/fe310/eos/eos.c
+++ b/fw/fe310/eos/eos.c
@@ -28,13 +28,13 @@ void eos_init(void) {
     int touch_calibrate = 0;
     int rv;
 
-    printf("INIT:%d\n", wakeup_cause);
-
     eos_evtq_init(wakeup_cause);
     eos_intr_init(wakeup_cause);
     eos_timer_init(wakeup_cause);
     eos_uart_init(wakeup_cause);
 
+    printf("INIT:%d\n", wakeup_cause);
+
     rv = eos_pwr_init(wakeup_cause);
     if (rv) printf("PWR INIT ERR:%d\n", rv);
     rv = eos_i2s_init(wakeup_cause);
diff --git a/fw/fe310/eos/eve/eve.c b/fw/fe310/eos/eve/eve.c
index 91458d8..e1191fc 100644
--- a/fw/fe310/eos/eve/eve.c
+++ b/fw/fe310/eos/eve/eve.c
@@ -458,27 +458,27 @@ void eve_brightness(uint8_t b) {
 
 int eve_gpio_get(int gpio) {
     uint16_t reg = eve_read16(REG_GPIOX);
+
     return !!(reg & (1 << gpio));
 }
 
 void eve_gpio_set(int gpio, int val) {
     uint16_t reg = eve_read16(REG_GPIOX);
     uint16_t reg_val = (1 << gpio);
-    if (val) {
-        reg |= reg_val;
-    } else {
-        reg &= ~reg_val;
-    }
+
+    reg = val ? reg | reg_val : reg & ~reg_val;
     eve_write16(REG_GPIOX, reg);
 }
 
 uint8_t eve_gpio_get_dir(void) {
     uint16_t reg = eve_read16(REG_GPIOX_DIR);
+
     return reg & 0x000f;
 }
 
 void eve_gpio_set_dir(uint8_t dir) {
     uint16_t reg = eve_read16(REG_GPIOX_DIR);
+
     reg &= 0xfff0;
     reg |= dir & 0x0f;
     eve_write16(REG_GPIOX_DIR, reg);
diff --git a/fw/fe310/eos/i2s.c b/fw/fe310/eos/i2s.c
index 5e5eaa7..9cc32e0 100644
--- a/fw/fe310/eos/i2s.c
+++ b/fw/fe310/eos/i2s.c
@@ -28,6 +28,8 @@
 EOSABuf _eos_i2s_mic_buf;
 EOSABuf _eos_i2s_spk_buf;
 uint32_t _eos_i2s_fmt = 0;
+uint32_t _eos_i2s_mode = 0;
+uint32_t _eos_i2s_sample = 0;
 uint32_t _eos_i2s_mic_wm = 0;
 uint32_t _eos_i2s_spk_wm = 0;
 uint32_t _eos_i2s_mic_evt_enable = 0;
@@ -102,12 +104,14 @@ static void i2s_handle_evt(unsigned char type, unsigned char *buffer, uint16_t l
             _eos_i2s_mic_evt_enable = 1;
             set_csr(mstatus, MSTATUS_MIE);
             break;
+
         case EOS_I2S_ETYPE_SPK:
             if (i2s_spk_handler) i2s_spk_handler(type);
             clear_csr(mstatus, MSTATUS_MIE);
             _eos_i2s_spk_evt_enable = 1;
             set_csr(mstatus, MSTATUS_MIE);
             break;
+
         default:
             eos_evtq_bad_handler(type, buffer, len);
             break;
@@ -125,12 +129,10 @@ static void _spk_vol_set(uint8_t vol) {
     if (spk_cmp <= 0) {
         I2S_REG_WS_SPK(PWM_CMP1) = i2s_clk_period * (32 + spk_cmp);
         I2S_REG_WS_SPK(PWM_CMP2) = i2s_clk_period * (64 + spk_cmp);
-        I2S_REG_WS_SPK(PWM_CMP3) = i2s_clk_period * 33;
         GPIO_REG(GPIO_OUTPUT_XOR) &= ~(1 << I2S_PIN_WS_SPK);
     } else {
         I2S_REG_WS_SPK(PWM_CMP1) = i2s_clk_period * spk_cmp;
         I2S_REG_WS_SPK(PWM_CMP2) = i2s_clk_period * (32 + spk_cmp);
-        I2S_REG_WS_SPK(PWM_CMP3) = i2s_clk_period * (33 + spk_cmp);
         GPIO_REG(GPIO_OUTPUT_XOR) |= (1 << I2S_PIN_WS_SPK);
     }
 }
@@ -140,6 +142,10 @@ extern void _eos_i2s_start_pwm(void);
 int eos_i2s_init(uint8_t wakeup_cause) {
     eos_evtq_set_handler(EOS_EVT_I2S, i2s_handle_evt);
 
+    I2S_REG_CK(PWM_CFG)         = 0;
+    I2S_REG_WS_MIC(PWM_CFG)     = 0;
+    I2S_REG_WS_SPK(PWM_CFG)     = 0;
+
     eos_i2s_init_mux();
 
     GPIO_REG(GPIO_OUTPUT_VAL)   |=  (1 << I2S_PIN_CK_SW);
@@ -202,10 +208,10 @@ void eos_i2s_start(uint32_t sample_rate, unsigned char fmt) {
     _eos_i2s_mic_evt_enable = 1;
     _eos_i2s_spk_evt_enable = 1;
 
+    eos_intr_set_priority(I2S_IRQ_SD_ID, IRQ_PRIORITY_I2S_SD);
     eos_intr_set_priority(I2S_IRQ_WS_ID, IRQ_PRIORITY_I2S_WS);
-    eos_intr_set_priority(I2S_IRQ_SD_ID, 0);
-    eos_intr_enable(I2S_IRQ_WS_ID);
     eos_intr_enable(I2S_IRQ_SD_ID);
+    eos_intr_enable(I2S_IRQ_WS_ID);
 
     _eos_i2s_start_pwm();
     /*
@@ -214,9 +220,13 @@ void eos_i2s_start(uint32_t sample_rate, unsigned char fmt) {
     I2S_REG_WS_SPK(PWM_CFG)     = PWM_CFG_ENALWAYS | PWM_CFG_ZEROCMP | PWM_CFG_CMP1GANG;
     */
 
-    GPIO_REG(GPIO_OUTPUT_VAL)   |= (1 << I2S_PIN_CK_SR);
-    GPIO_REG(GPIO_IOF_SEL)      |= I2S_PIN_PWM;
-    GPIO_REG(GPIO_IOF_EN)       |= I2S_PIN_PWM;
+    GPIO_REG(GPIO_OUTPUT_VAL)   |=  (1 << I2S_PIN_WS_MIC);
+    GPIO_REG(GPIO_INPUT_EN)     &= ~(1 << I2S_PIN_WS_MIC);
+    GPIO_REG(GPIO_OUTPUT_EN)    |=  (1 << I2S_PIN_WS_MIC);
+
+    GPIO_REG(GPIO_OUTPUT_VAL)   |=  (1 << I2S_PIN_CK_SR);
+    GPIO_REG(GPIO_IOF_SEL)      |=  I2S_PIN_PWM;
+    GPIO_REG(GPIO_IOF_EN)       |=  I2S_PIN_PWM;
 }
 
 void eos_i2s_stop(void) {
@@ -229,8 +239,7 @@ void eos_i2s_stop(void) {
 
     _eos_i2s_mic_evt_enable = 0;
     _eos_i2s_spk_evt_enable = 0;
-    eos_intr_set_priority(I2S_IRQ_WS_ID, 0);
-    eos_intr_set_priority(I2S_IRQ_SD_ID, 0);
+
     eos_intr_disable(I2S_IRQ_WS_ID);
     eos_intr_disable(I2S_IRQ_SD_ID);
 
@@ -239,10 +248,13 @@ void eos_i2s_stop(void) {
 
     GPIO_REG(GPIO_OUTPUT_XOR)   &= ~(1 << I2S_PIN_WS_SPK);
     GPIO_REG(GPIO_IOF_EN)       &= ~I2S_PIN_PWM;
+
+    eos_i2s_mic_set_wm(0);
+    eos_i2s_spk_set_wm(0);
 }
 
 int eos_i2s_running(void) {
-    return !!(GPIO_REG(GPIO_IOF_EN) & I2S_PIN_PWM);
+    return !!(GPIO_REG(GPIO_IOF_EN) & (1 << I2S_PIN_CK));
 }
 
 void eos_i2s_mic_init(uint8_t *mic_arr, uint16_t mic_arr_size) {
@@ -291,16 +303,22 @@ uint16_t eos_i2s_mic_read(uint8_t *sample, uint16_t ssize) {
 }
 
 int eos_i2s_mic_pop8(uint8_t *sample) {
+    int ret;
+
     clear_csr(mstatus, MSTATUS_MIE);
-    int ret = _abuf_pop8(&_eos_i2s_mic_buf, sample);
+    ret = _abuf_pop8(&_eos_i2s_mic_buf, sample);
     set_csr(mstatus, MSTATUS_MIE);
+
     return ret;
 }
 
 int eos_i2s_mic_pop16(uint16_t *sample) {
+    int ret;
+
     clear_csr(mstatus, MSTATUS_MIE);
-    int ret = _abuf_pop16(&_eos_i2s_mic_buf, sample);
+    ret = _abuf_pop16(&_eos_i2s_mic_buf, sample);
     set_csr(mstatus, MSTATUS_MIE);
+
     return ret;
 }
 
@@ -364,16 +382,22 @@ uint16_t eos_i2s_spk_write(uint8_t *sample, uint16_t ssize) {
 }
 
 int eos_i2s_spk_push8(uint8_t sample) {
+    int ret;
+
     clear_csr(mstatus, MSTATUS_MIE);
-    int ret = _abuf_push8(&_eos_i2s_spk_buf, sample);
+    ret = _abuf_push8(&_eos_i2s_spk_buf, sample);
     set_csr(mstatus, MSTATUS_MIE);
+
     return ret;
 }
 
 int eos_i2s_spk_push16(uint16_t sample) {
+    int ret;
+
     clear_csr(mstatus, MSTATUS_MIE);
-    int ret = _abuf_push16(&_eos_i2s_spk_buf, sample);
+    ret = _abuf_push16(&_eos_i2s_spk_buf, sample);
     set_csr(mstatus, MSTATUS_MIE);
+
     return ret;
 }
 
diff --git a/fw/fe310/eos/trap_entry.S b/fw/fe310/eos/trap_entry.S
index 96024cb..506683b 100644
--- a/fw/fe310/eos/trap_entry.S
+++ b/fw/fe310/eos/trap_entry.S
@@ -37,7 +37,7 @@
 
 .global eos_trap_entry
 eos_trap_entry:
-  addi sp, sp, -8*REGBYTES
+  addi sp, sp, -10*REGBYTES
   STORE x8, 0*REGBYTES(sp)
   STORE x9, 1*REGBYTES(sp)
   STORE x18, 2*REGBYTES(sp)
@@ -46,6 +46,8 @@ eos_trap_entry:
   STORE x21, 5*REGBYTES(sp)
   STORE x22, 6*REGBYTES(sp)
   STORE x23, 7*REGBYTES(sp)
+  STORE x24, 8*REGBYTES(sp)
+  STORE x25, 9*REGBYTES(sp)
 
   csrr x8, mcause
   li x18, MCAUSE_EXT
@@ -84,34 +86,62 @@ evtq_push:
   jalr x0, x21
 
 i2s_handle_sd:
-  # exit if too early
-  li x18, I2S_CTRL_ADDR_WS_SPK
-  lw x8, PWM_COUNT(x18)
-  lw x9, PWM_CMP3(x18)
-  bltu x8, x9, i2s_handle_sd_exit
+  li x9, I2S_CTRL_ADDR_WS_MIC
+  lw x18, PWM_COUNT(x9)
+  lw x19, PWM_CMP1(x9)
+  lw x20, PWM_CMP2(x9)
+  lw x21, PWM_CMP3(x9)
 
-  # disable sd irq
-  li x18, PLIC_PRIORITY
-  sw x0, 4*I2S_IRQ_SD_ID(x18)
+  # exit if too early
+  bltu x18, x21, i2s_sd_exit
+
+  # move CMPs for next channel and store parity to x25
+  li x25, 0
+  bltu x20, x19, 0f
+  li x25, 1
+  neg x19, x19
+  li x22, PLIC_PRIORITY
+  sw x0, 4*I2S_IRQ_SD_ID(x22)
+0:
+  add x20, x20, x19
+  add x21, x21, x19
+  sw x20, PWM_CMP2(x9)
+  sw x21, PWM_CMP3(x9)
 
   la x9, _eos_i2s_fmt
-  lw x23, 0(x9)
+  lw x24, 0(x9)
 
 i2s_abuf_pop:
-  # pop from spk buf -> x8
+  la x9, _eos_i2s_mode
+  lw x22, 0(x9)
+  and x22, x22, x25
+  beqz x22, 0f
+
+  la x9, _eos_i2s_sample
+  lw x8, 0(x9)
+  j i2s_sd_xchg
+
+0:
   mv x8, x0
+
+  # check for pop from spk buf
+  la x9, _eos_i2s_spk_wm
+  lw x22, 0(x9)
+  beqz x22, i2s_sd_xchg
+
+  # pop from spk buf -> x8
   la x9, _eos_i2s_spk_buf
   lhu x18, I2S_ABUF_OFF_IDXR(x9)
   lhu x19, I2S_ABUF_OFF_IDXW(x9)
   lhu x20, I2S_ABUF_OFF_SIZE(x9)
 
-  beq x18, x19, i2s_handle_sd_xchg
+  beq x18, x19, 2f
 
   addi x20, x20, -1
   and x20, x20, x18
   lw x21, I2S_ABUF_OFF_ARRAY(x9)
   add x21, x21, x20
-  beqz x23, 0f
+  beqz x24, 0f
   lbu x8, 0(x21)
   addi x18, x18, 1
   j 1f
@@ -124,15 +154,13 @@ i2s_abuf_pop:
 1:
   sh x18, I2S_ABUF_OFF_IDXR(x9)
 
+2:
   li x21, 0xffff
   sub x18, x19, x18
   and x18, x18, x21
 
   # check for push to event queue
-  la x9, _eos_i2s_spk_wm
-  lw x20, 0(x9)
-  beqz x20, i2s_decode
-  bgtu x18, x20, i2s_decode
+  bgtu x18, x22, i2s_decode
 
   la x9, _eos_i2s_spk_evt_enable
   lw x18, 0(x9)
@@ -146,7 +174,7 @@ i2s_abuf_pop:
   sb x18, MSGQ_ITEM_OFF_TYPE(x21)
 
 i2s_decode:
-  beqz x23, i2s_handle_sd_xchg
+  beqz x24, 3f
   # aLaw decode -> x8
   xori x8, x8, 0x55
   andi x9, x8, 0x80
@@ -181,10 +209,13 @@ i2s_decode:
   slli x8, x8, 1
   ori x8, x8, 1
 2:
-  beqz x9, i2s_handle_sd_xchg
+  beqz x9, 3f
   mul x8, x8, x9
+3:
+  la x9, _eos_i2s_sample
+  sw x8, 0(x9)
 
-i2s_handle_sd_xchg:
+i2s_sd_xchg:
   # read/write shift reg: x8 -> sr -> x8
   li x18, GPIO_CTRL_ADDR
   li x19, (0x1 << I2S_PIN_SD_IN)
@@ -234,17 +265,15 @@ i2s_handle_sd_xchg:
   xor x22, x22, x21
   sw x22, GPIO_OUTPUT_VAL(x18)
 
+  addi x23, x23, -1
+  bnez x23, 0b
+
   # idle
   li x9, I2S_IDLE_CYCLES
 1:
   addi x9, x9, -1
   bnez x9, 1b
 
-  addi x23, x23, -1
-  beqz x23, 2f
-  j 0b
-
-2:
   # 74HC595 ck low (I2S_PIN_CK_SR high)
   xor x22, x22, x21
   sw x22, GPIO_OUTPUT_VAL(x18)
@@ -256,11 +285,8 @@ i2s_handle_sd_xchg:
   slli x8, x8, 16
   srai x8, x8, 16
 
-  la x9, _eos_i2s_fmt
-  lw x23, 0(x9)
-
 i2s_encode:
-  beqz x23, i2s_abuf_push
+  beqz x24, i2s_abuf_push
   # aLaw encode -> x8
   li x18, 0x800
   li x19, 7
@@ -292,6 +318,14 @@ i2s_encode:
   andi x8, x8, 0xff
 
 i2s_abuf_push:
+  # check parity
+  # bnez x25, i2s_sd_exit
+
+  # check for push to mic buf
+  la x9, _eos_i2s_mic_wm
+  lw x22, 0(x9)
+  beqz x22, i2s_sd_exit
+
   # push to mic buf
   la x9, _eos_i2s_mic_buf
   lhu x18, I2S_ABUF_OFF_IDXR(x9)
@@ -301,13 +335,13 @@ i2s_abuf_push:
 
   sub x18, x19, x18
   and x18, x18, x21
-  beq x18, x20, i2s_handle_sd_exit
+  beq x18, x20, 2f
 
   addi x20, x20, -1
   and x20, x20, x19
   lw x21, I2S_ABUF_OFF_ARRAY(x9)
   add x21, x21, x20
-  beqz x23, 0f
+  beqz x24, 0f
   sb x8, 0(x21)
   addi x19, x19, 1
   addi x18, x18, 1
@@ -321,24 +355,22 @@ i2s_abuf_push:
 1:
   sh x19, I2S_ABUF_OFF_IDXW(x9)
 
+2:
   # check for push to event queue
-  la x9, _eos_i2s_mic_wm
-  lw x20, 0(x9)
-  beqz x20, i2s_handle_sd_exit
-  bltu x18, x20, i2s_handle_sd_exit
+  bltu x18, x22, i2s_sd_exit
 
   la x9, _eos_i2s_mic_evt_enable
   lw x18, 0(x9)
-  beqz x18, i2s_handle_sd_exit
+  beqz x18, i2s_sd_exit
   sw x0, 0(x9)
 
   # push to event queue
   jal x22, evtq_push
-  beqz x21, i2s_handle_sd_exit
+  beqz x21, i2s_sd_exit
   li x18, (EOS_EVT_I2S | EOS_I2S_ETYPE_MIC)
   sb x18, MSGQ_ITEM_OFF_TYPE(x21)
 
-i2s_handle_sd_exit:
+i2s_sd_exit:
   # complete
   li x18, I2S_IRQ_SD_ID
   li x19, PLIC_CLAIM
@@ -418,7 +450,9 @@ trap_exit_data:
   LOAD x21, 5*REGBYTES(sp)
   LOAD x22, 6*REGBYTES(sp)
   LOAD x23, 7*REGBYTES(sp)
-  addi sp, sp, 8*REGBYTES
+  LOAD x24, 8*REGBYTES(sp)
+  LOAD x25, 9*REGBYTES(sp)
+  addi sp, sp, 10*REGBYTES
 
   mret
 
@@ -431,7 +465,7 @@ handle_intr:
   .align 4
 
 trap_entry_text:
-  addi sp, sp, -24*REGBYTES
+  addi sp, sp, -22*REGBYTES
 
   STORE x1, 0*REGBYTES(sp)
   STORE x2, 1*REGBYTES(sp)
@@ -448,14 +482,12 @@ trap_entry_text:
   STORE x15, 12*REGBYTES(sp)
   STORE x16, 13*REGBYTES(sp)
   STORE x17, 14*REGBYTES(sp)
-  STORE x24, 15*REGBYTES(sp)
-  STORE x25, 16*REGBYTES(sp)
-  STORE x26, 17*REGBYTES(sp)
-  STORE x27, 18*REGBYTES(sp)
-  STORE x28, 19*REGBYTES(sp)
-  STORE x29, 20*REGBYTES(sp)
-  STORE x30, 21*REGBYTES(sp)
-  STORE x31, 22*REGBYTES(sp)
+  STORE x26, 15*REGBYTES(sp)
+  STORE x27, 16*REGBYTES(sp)
+  STORE x28, 17*REGBYTES(sp)
+  STORE x29, 18*REGBYTES(sp)
+  STORE x30, 19*REGBYTES(sp)
+  STORE x31, 20*REGBYTES(sp)
 
   li x18, MCAUSE_TIMER
   beq x8, x18, handle_timer
@@ -473,7 +505,6 @@ handle_ext:
   call eos_intr_handle
   li x18, PLIC_CLAIM
   sw a0, 0(x18)
-  j trap_exit_text
 
 trap_exit_text:
   # Remain in M-mode after mret
@@ -495,23 +526,23 @@ trap_exit_text:
   LOAD x15, 12*REGBYTES(sp)
   LOAD x16, 13*REGBYTES(sp)
   LOAD x17, 14*REGBYTES(sp)
-  LOAD x24, 15*REGBYTES(sp)
-  LOAD x25, 16*REGBYTES(sp)
-  LOAD x26, 17*REGBYTES(sp)
-  LOAD x27, 18*REGBYTES(sp)
-  LOAD x28, 19*REGBYTES(sp)
-  LOAD x29, 20*REGBYTES(sp)
-  LOAD x30, 21*REGBYTES(sp)
-  LOAD x31, 22*REGBYTES(sp)
-
-  LOAD x8, 24*REGBYTES(sp)
-  LOAD x9, 25*REGBYTES(sp)
-  LOAD x18, 26*REGBYTES(sp)
-  LOAD x19, 27*REGBYTES(sp)
-  LOAD x20, 28*REGBYTES(sp)
-  LOAD x21, 29*REGBYTES(sp)
-  LOAD x22, 30*REGBYTES(sp)
-  LOAD x23, 31*REGBYTES(sp)
+  LOAD x26, 15*REGBYTES(sp)
+  LOAD x27, 16*REGBYTES(sp)
+  LOAD x28, 17*REGBYTES(sp)
+  LOAD x29, 18*REGBYTES(sp)
+  LOAD x30, 19*REGBYTES(sp)
+  LOAD x31, 20*REGBYTES(sp)
+
+  LOAD x8, 22*REGBYTES(sp)
+  LOAD x9, 23*REGBYTES(sp)
+  LOAD x18, 24*REGBYTES(sp)
+  LOAD x19, 25*REGBYTES(sp)
+  LOAD x20, 26*REGBYTES(sp)
+  LOAD x21, 27*REGBYTES(sp)
+  LOAD x22, 28*REGBYTES(sp)
+  LOAD x23, 29*REGBYTES(sp)
+  LOAD x24, 30*REGBYTES(sp)
+  LOAD x25, 31*REGBYTES(sp)
 
   addi sp, sp, 32*REGBYTES
   mret
-- 
cgit v1.2.3